diff --git a/account/manager_test.go b/account/manager_test.go
index bda6e2d3a..8b172a477 100644
--- a/account/manager_test.go
+++ b/account/manager_test.go
@@ -19,13 +19,19 @@
package account
import (
+ "crypto/md5"
+ "fmt"
"testing"
+ "time"
"os"
"github.com/nebulasio/go-nebulas/core"
+ "github.com/nebulasio/go-nebulas/crypto"
+ "github.com/nebulasio/go-nebulas/crypto/hash"
"github.com/nebulasio/go-nebulas/crypto/keystore"
"github.com/nebulasio/go-nebulas/util"
+ "github.com/nebulasio/go-nebulas/util/byteutils"
"github.com/stretchr/testify/assert"
)
@@ -223,3 +229,161 @@ func TestManager_SignTransactionWithPassphrase(t *testing.T) {
})
}
}
+
+func TestForCryptoJS(t *testing.T) {
+ type args struct {
+ method string
+ input string
+ output string
+ }
+
+ tests := []args{
+ {
+ method: "sha256",
+ input: "Nebulas is a next generation public blockchain, aiming for a continuously improving ecosystem.",
+ output: "a32d6d686968192663b9c9e21e6a3ba1ba9b2e288470c2f98b790256530933e0",
+ },
+
+ {
+ method: "sha3256",
+ input: "Nebulas is a next generation public blockchain, aiming for a continuously improving ecosystem.",
+ output: "564733f9f3e139b925cfb1e7e50ba8581e9107b13e4213f2e4708d9c284be75b",
+ },
+
+ {
+ method: "ripemd160",
+ input: "Nebulas is a next generation public blockchain, aiming for a continuously improving ecosystem.",
+ output: "4236aa9974eb7b9ddb0f7a7ed06d4bf3d9c0e386",
+ },
+
+ {
+ method: "md5",
+ input: "Nebulas is a next generation public blockchain, aiming for a continuously improving ecosystem.",
+ output: "9954125a33a380c3117269cff93f76a7",
+ },
+
+ {
+ method: "base64",
+ input: "Nebulas is a next generation public blockchain, aiming for a continuously improving ecosystem.",
+ output: "TmVidWxhcyBpcyBhIG5leHQgZ2VuZXJhdGlvbiBwdWJsaWMgYmxvY2tjaGFpbiwgYWltaW5nIGZvciBhIGNvbnRpbnVvdXNseSBpbXByb3ZpbmcgZWNvc3lzdGVtLg==",
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.method, func(t *testing.T) {
+ out := ""
+ switch tt.method {
+ case "sha256":
+ out = byteutils.Hex(hash.Sha256([]byte(tt.input)))
+ case "sha3256":
+ out = byteutils.Hex(hash.Sha3256([]byte(tt.input)))
+ case "ripemd160":
+ out = byteutils.Hex(hash.Ripemd160([]byte(tt.input)))
+ case "md5":
+ r := md5.Sum([]byte(tt.input))
+ out = byteutils.Hex(r[:])
+ case "base64":
+ out = string(hash.Base64Encode([]byte(tt.input)))
+ }
+ assert.Equal(t, tt.output, out)
+ })
+ }
+
+ // recoverAddress
+ manager, _ := NewManager(nil)
+ addr, err := manager.NewAccount([]byte("passphrase"))
+ assert.Nil(t, err, "new address err")
+ err = manager.Unlock(addr, []byte("passphrase"), keystore.DefaultUnlockDuration)
+ assert.Nil(t, err, "unlock err")
+ key, err := manager.ks.GetUnlocked(addr.String())
+ assert.Nil(t, err, "get key err")
+
+ signature, err := crypto.NewSignature(1)
+ assert.Nil(t, err, "get signature err")
+
+ err = signature.InitSign(key.(keystore.PrivateKey))
+ assert.Nil(t, err, "init signature err")
+
+ data := hash.Sha3256([]byte("Nebulas is a next generation public blockchain, aiming for a continuously improving ecosystem."))
+ assert.Equal(t, "564733f9f3e139b925cfb1e7e50ba8581e9107b13e4213f2e4708d9c284be75b", byteutils.Hex(data))
+ signData, err := signature.Sign(data)
+ assert.Nil(t, err, "sign data err")
+ signHex := byteutils.Hex(signData)
+ // assert.Equal(t, "d80e282d165f8c05d8581133df7af3c7c41d51ec7cd8470c18b84a31b9af6a9d1da876ab28a88b0226707744679d4e180691aca6bdef5827622396751a0670c101", byteutils.Hex(signData))
+
+ // recover
+ rb, err := byteutils.FromHex(signHex)
+ assert.Nil(t, err, "from hex error")
+ rAddr, err := core.RecoverSignerFromSignature(keystore.Algorithm(1), data, rb)
+ assert.Nil(t, err, "recover err")
+ assert.Equal(t, addr.String(), rAddr.String())
+
+ acc, err := manager.getAccount(addr)
+ assert.Nil(t, err, "get acc err")
+ err = manager.Remove(addr, []byte("passphrase"))
+ assert.Nil(t, err)
+ err = os.Remove(acc.path)
+ assert.Nil(t, err)
+}
+func TestCryptoPerformance(t *testing.T) {
+ // test sha256
+ start := time.Now()
+ out := ""
+ for i := 0; i < 1000; i++ {
+ out = byteutils.Hex(hash.Sha256([]byte("Nebulas is a next generation public blockchain, aiming for a continuously improving ecosystem.")))
+ }
+ cost := time.Now().Sub(start).Nanoseconds()
+ fmt.Println("sha256", out, cost)
+
+ // test md5
+ start = time.Now()
+ for i := 0; i < 1000; i++ {
+ r := md5.Sum([]byte("Nebulas is a next generation public blockchain, aiming for a continuously improving ecosystem."))
+ out = byteutils.Hex(r[:])
+ }
+ cost = time.Now().Sub(start).Nanoseconds()
+ fmt.Println("md5", out, cost)
+
+ // test recover address
+
+ manager, _ := NewManager(nil)
+ addr, err := manager.NewAccount([]byte("passphrase"))
+ assert.Nil(t, err, "new address err")
+ err = manager.Unlock(addr, []byte("passphrase"), keystore.DefaultUnlockDuration)
+ assert.Nil(t, err, "unlock err")
+ key, err := manager.ks.GetUnlocked(addr.String())
+ assert.Nil(t, err, "get key err")
+
+ signature, err := crypto.NewSignature(1)
+ assert.Nil(t, err, "get signature err")
+
+ err = signature.InitSign(key.(keystore.PrivateKey))
+ assert.Nil(t, err, "init signature err")
+
+ data := hash.Sha3256([]byte("Nebulas is a next generation public blockchain, aiming for a continuously improving ecosystem."))
+ assert.Equal(t, "564733f9f3e139b925cfb1e7e50ba8581e9107b13e4213f2e4708d9c284be75b", byteutils.Hex(data))
+ signData, err := signature.Sign(data)
+ assert.Nil(t, err, "sign data err")
+ signHex := byteutils.Hex(signData)
+
+ // recover
+ rb, err := byteutils.FromHex(signHex)
+ assert.Nil(t, err, "from hex error")
+ rAddr, err := core.RecoverSignerFromSignature(keystore.Algorithm(1), data, rb)
+ start = time.Now()
+ for i := 0; i < 1000; i++ {
+ core.RecoverSignerFromSignature(keystore.Algorithm(1), data, rb)
+ }
+ cost = time.Now().Sub(start).Nanoseconds()
+ fmt.Println("recoverAddress", rAddr.String(), cost)
+
+ assert.Nil(t, err, "recover err")
+ assert.Equal(t, addr.String(), rAddr.String())
+
+ acc, err := manager.getAccount(addr)
+ assert.Nil(t, err, "get acc err")
+ err = manager.Remove(addr, []byte("passphrase"))
+ assert.Nil(t, err)
+ err = os.Remove(acc.path)
+ assert.Nil(t, err)
+}
diff --git a/cmd/v8/main.go b/cmd/v8/main.go
index 6f34bac37..855ffd96c 100644
--- a/cmd/v8/main.go
+++ b/cmd/v8/main.go
@@ -24,6 +24,7 @@ import (
"os"
"time"
+ "github.com/nebulasio/go-nebulas/core"
"github.com/nebulasio/go-nebulas/core/state"
"github.com/nebulasio/go-nebulas/nf/nvm"
"github.com/nebulasio/go-nebulas/storage"
@@ -34,9 +35,9 @@ func main() {
mem, _ := storage.NewMemoryStorage()
context, _ := state.NewWorldState(nil, mem)
- contract, _ := context.CreateContractAccount([]byte("account2"), nil)
+ contract, _ := context.CreateContractAccount([]byte("account2"), nil, nil)
- ctx, err := nvm.NewContext(nil, nil, contract, context)
+ ctx, err := nvm.NewContext(core.MockBlock(nil, 1), nil, contract, context)
if err == nil {
engine := nvm.NewV8Engine(ctx)
result, err := engine.RunScriptSource(string(data), 0)
diff --git a/consensus/dpos/dpos.go b/consensus/dpos/dpos.go
index 521cf1dd2..4b2065629 100644
--- a/consensus/dpos/dpos.go
+++ b/consensus/dpos/dpos.go
@@ -634,6 +634,11 @@ func (dpos *Dpos) mintBlock(now int64) error {
deadlineInMs, err := dpos.checkDeadline(tail, nowInMs)
if err != nil {
+ logging.VLog().WithFields(logrus.Fields{
+ "tail": tail,
+ "now": nowInMs,
+ "err": err,
+ }).Debug("checkDeadline")
return err
}
diff --git a/core/block.go b/core/block.go
index cc31f3afd..c9d8df337 100644
--- a/core/block.go
+++ b/core/block.go
@@ -1298,3 +1298,11 @@ func LoadBlockFromStorage(hash byteutils.Hash, chain *BlockChain) (*Block, error
block.storage = chain.storage
return block, nil
}
+
+// MockBlock nf/nvm/engine.CheckV8Run() & cmd/v8/main.go
+func MockBlock(header *BlockHeader, height uint64) *Block {
+ return &Block{
+ header: header,
+ height: height,
+ }
+}
diff --git a/core/blockchain.go b/core/blockchain.go
index 6a0b98069..0f29372cd 100644
--- a/core/blockchain.go
+++ b/core/blockchain.go
@@ -584,6 +584,7 @@ func (bc *BlockChain) StartActiveSync() bool {
return false
}
+// IsActiveSyncing returns true if being syncing
func (bc *BlockChain) IsActiveSyncing() bool {
return bc.syncService.IsActiveSyncing()
}
diff --git a/core/compatibility.go b/core/compatibility.go
index e4bb8d524..e57e1800d 100644
--- a/core/compatibility.go
+++ b/core/compatibility.go
@@ -19,6 +19,14 @@
package core
import (
+ "bytes"
+ "errors"
+ "os"
+ "path/filepath"
+ "sort"
+ "strconv"
+ "strings"
+
"github.com/nebulasio/go-nebulas/util/logging"
"github.com/sirupsen/logrus"
)
@@ -31,6 +39,76 @@ const (
TestNetID uint32 = 1001
)
+/********** js lib relative BEGIN **********/
+const (
+ // DefaultV8JSLibVersion default version
+ DefaultV8JSLibVersion = "1.0.0"
+)
+
+type version struct {
+ major, minor, patch int
+}
+
+type heightOfVersionSlice []*struct {
+ version string
+ height uint64
+}
+
+func (h heightOfVersionSlice) String() string {
+ var buf bytes.Buffer
+ buf.WriteString("{")
+ for _, v := range h {
+ if buf.Len() > 1 {
+ buf.WriteString(",")
+ }
+ buf.WriteString(v.version + "=" + strconv.FormatUint(v.height, 10))
+ }
+ buf.WriteString("}")
+ return buf.String()
+}
+func (h heightOfVersionSlice) Len() int {
+ return len(h)
+}
+func (h heightOfVersionSlice) Less(i, j int) bool {
+ return h[i].height < h[j].height
+}
+func (h heightOfVersionSlice) Swap(i, j int) {
+ h[i], h[j] = h[j], h[i]
+}
+
+// var ..
+var (
+ // NOTE: versions should be arranged in ascending order
+ // map[libname][versions]
+ V8JSLibs = map[string][]string{
+ "execution_env.js": {"1.0.0", "1.0.5"},
+ "bignumber.js": {"1.0.0"},
+ "random.js": {"1.0.0", "1.0.5"},
+ "date.js": {"1.0.0", "1.0.5"},
+ "tsc.js": {"1.0.0"},
+ "util.js": {"1.0.0"},
+ "esprima.js": {"1.0.0"},
+ "assert.js": {"1.0.0"},
+ "instruction_counter.js": {"1.0.0"},
+ "typescriptServices.js": {"1.0.0"},
+ "blockchain.js": {"1.0.0", "1.0.5"},
+ "console.js": {"1.0.0"},
+ "event.js": {"1.0.0"},
+ "storage.js": {"1.0.0"},
+ "crypto.js": {"1.0.5"},
+ "uint.js": {"1.0.5"},
+ }
+
+ digitalized = make(map[string][]*version)
+)
+
+var (
+ // ErrInvalidJSLibVersion ..
+ ErrInvalidJSLibVersion = errors.New("invalid js lib version")
+)
+
+/********** js lib relative END **********/
+
// others, e.g. local/develop
const (
// LocalTransferFromContractEventRecordableHeight
@@ -53,6 +131,22 @@ const (
//LocalWdResetRecordDependencyHeight
LocalWsResetRecordDependencyHeight uint64 = 2
+
+ // LocalV8JSLibVersionControlHeight
+ LocalV8JSLibVersionControlHeight uint64 = 2
+
+ //LocalNetTransferFromContractFailureEventRecordableHeight
+ LocalTransferFromContractFailureEventRecordableHeight uint64 = 2
+
+ //LocalNetNewNvmExeTimeoutConsumeGasHeight
+ LocalNewNvmExeTimeoutConsumeGasHeight uint64 = 2
+)
+
+// var for local/develop
+var (
+ LocalV8JSLibVersionHeightSlice = heightOfVersionSlice{
+ {"1.0.5", LocalV8JSLibVersionControlHeight},
+ }
)
// TestNet
@@ -77,6 +171,22 @@ const (
//TestNetWdResetRecordDependencyHeight
TestNetWsResetRecordDependencyHeight uint64 = 281800
+
+ // TestNetV8JSLibVersionControlHeight
+ TestNetV8JSLibVersionControlHeight uint64 = 424300
+
+ //TestNetTransferFromContractFailureEventRecordableHeight
+ TestNetTransferFromContractFailureEventRecordableHeight uint64 = 424300
+
+ //TestNetNewNvmExeTimeoutConsumeGasHeight
+ TestNetNewNvmExeTimeoutConsumeGasHeight uint64 = 424300
+)
+
+// var for TestNet
+var (
+ TestNetV8JSLibVersionHeightSlice = heightOfVersionSlice{
+ {"1.0.5", TestNetV8JSLibVersionControlHeight},
+ }
)
// MainNet
@@ -101,6 +211,22 @@ const (
//MainNetWdResetRecordDependencyHeight
MainNetWsResetRecordDependencyHeight uint64 = 325666
+
+ // MainNetV8JSLibVersionControlHeight
+ MainNetV8JSLibVersionControlHeight uint64 = 460000
+
+ //MainNetTransferFromContractFailureEventRecordableHeight
+ MainNetTransferFromContractFailureEventRecordableHeight uint64 = 460000
+
+ //MainNetNewNvmExeTimeoutConsumeGasHeight
+ MainNetNewNvmExeTimeoutConsumeGasHeight uint64 = 460000
+)
+
+// var for MainNet
+var (
+ MainNetV8JSLibVersionHeightSlice = heightOfVersionSlice{
+ {"1.0.5", MainNetV8JSLibVersionControlHeight},
+ }
)
var (
@@ -122,8 +248,20 @@ var (
// NvmMemoryLimitWithoutInjectHeight memory of nvm contract without inject code
NvmMemoryLimitWithoutInjectHeight = TestNetNvmMemoryLimitWithoutInjectHeight
- //WdResetRecordDependencyHeight if tx execute faied, worldstate reset and need to record to address dependency
+ //WsResetRecordDependencyHeight if tx execute faied, worldstate reset and need to record to address dependency
WsResetRecordDependencyHeight = TestNetWsResetRecordDependencyHeight
+
+ // V8JSLibVersionControlHeight enable v8 js lib version control
+ V8JSLibVersionControlHeight = TestNetV8JSLibVersionControlHeight
+
+ // V8JSLibVersionHeightSlice all version-height pairs
+ V8JSLibVersionHeightSlice = TestNetV8JSLibVersionHeightSlice
+
+ // TransferFromContractFailureEventRecordableHeight record event 'TransferFromContractEvent' since this height
+ TransferFromContractFailureEventRecordableHeight = TestNetTransferFromContractFailureEventRecordableHeight
+
+ //NewNvmExeTimeoutConsumeGasHeight
+ NewNvmExeTimeoutConsumeGasHeight = TestNetNewNvmExeTimeoutConsumeGasHeight
)
// SetCompatibilityOptions set compatibility height according to chain_id
@@ -137,6 +275,10 @@ func SetCompatibilityOptions(chainID uint32) {
RecordCallContractResultHeight = MainNetRecordCallContractResultHeight
NvmMemoryLimitWithoutInjectHeight = MainNetNvmMemoryLimitWithoutInjectHeight
WsResetRecordDependencyHeight = MainNetWsResetRecordDependencyHeight
+ V8JSLibVersionControlHeight = MainNetV8JSLibVersionControlHeight
+ V8JSLibVersionHeightSlice = MainNetV8JSLibVersionHeightSlice
+ TransferFromContractFailureEventRecordableHeight = MainNetTransferFromContractFailureEventRecordableHeight
+ NewNvmExeTimeoutConsumeGasHeight = MainNetNewNvmExeTimeoutConsumeGasHeight
} else if chainID == TestNetID {
TransferFromContractEventRecordableHeight = TestNetTransferFromContractEventRecordableHeight
@@ -146,6 +288,10 @@ func SetCompatibilityOptions(chainID uint32) {
RecordCallContractResultHeight = TestNetRecordCallContractResultHeight
NvmMemoryLimitWithoutInjectHeight = TestNetNvmMemoryLimitWithoutInjectHeight
WsResetRecordDependencyHeight = TestNetWsResetRecordDependencyHeight
+ V8JSLibVersionControlHeight = TestNetV8JSLibVersionControlHeight
+ V8JSLibVersionHeightSlice = TestNetV8JSLibVersionHeightSlice
+ TransferFromContractFailureEventRecordableHeight = TestNetTransferFromContractFailureEventRecordableHeight
+ NewNvmExeTimeoutConsumeGasHeight = TestNetNewNvmExeTimeoutConsumeGasHeight
} else {
TransferFromContractEventRecordableHeight = LocalTransferFromContractEventRecordableHeight
@@ -155,7 +301,15 @@ func SetCompatibilityOptions(chainID uint32) {
RecordCallContractResultHeight = LocalRecordCallContractResultHeight
NvmMemoryLimitWithoutInjectHeight = LocalNvmMemoryLimitWithoutInjectHeight
WsResetRecordDependencyHeight = LocalWsResetRecordDependencyHeight
+ V8JSLibVersionControlHeight = LocalV8JSLibVersionControlHeight
+ V8JSLibVersionHeightSlice = LocalV8JSLibVersionHeightSlice
+ TransferFromContractFailureEventRecordableHeight = LocalTransferFromContractFailureEventRecordableHeight
+ NewNvmExeTimeoutConsumeGasHeight = LocalNewNvmExeTimeoutConsumeGasHeight
}
+
+ // sort V8JSLibVersionHeightSlice in descending order by height
+ sort.Sort(sort.Reverse(V8JSLibVersionHeightSlice))
+
logging.VLog().WithFields(logrus.Fields{
"chain_id": chainID,
"TransferFromContractEventRecordableHeight": TransferFromContractEventRecordableHeight,
@@ -165,5 +319,160 @@ func SetCompatibilityOptions(chainID uint32) {
"RecordCallContractResultHeight": RecordCallContractResultHeight,
"NvmMemoryLimitWithoutInjectHeight": NvmMemoryLimitWithoutInjectHeight,
"WsResetRecordDependencyHeight": WsResetRecordDependencyHeight,
+ "V8JSLibVersionControlHeight": V8JSLibVersionControlHeight,
+ "V8JSLibVersionHeightSlice": V8JSLibVersionHeightSlice,
+ "TransferFromContractFailureHeight": TransferFromContractFailureEventRecordableHeight,
+ "NewNvmExeTimeoutConsumeGasHeight": NewNvmExeTimeoutConsumeGasHeight,
}).Info("Set compatibility options.")
+
+ checkJSLib()
+}
+
+// FindLastNearestLibVersion ..
+func FindLastNearestLibVersion(deployVersion, libname string) string {
+ if len(deployVersion) == 0 || len(libname) == 0 {
+ logging.VLog().WithFields(logrus.Fields{
+ "libname": libname,
+ "deployVersion": deployVersion,
+ }).Error("empty arguments.")
+ return ""
+ }
+
+ if libs, ok := digitalized[libname]; ok {
+ v, err := parseVersion(deployVersion)
+ if err != nil {
+ logging.VLog().WithFields(logrus.Fields{
+ "err": err,
+ "deployVersion": deployVersion,
+ "lib": libname,
+ }).Debug("parse deploy version error.")
+ return ""
+ }
+ for i := len(libs) - 1; i >= 0; i-- {
+ if compareVersion(libs[i], v) <= 0 {
+ logging.VLog().WithFields(logrus.Fields{
+ "libname": libname,
+ "deployVersion": deployVersion,
+ "return": libs[i],
+ }).Debug("filter js lib.")
+ return V8JSLibs[libname][i]
+ }
+ }
+ } else {
+ logging.VLog().WithFields(logrus.Fields{
+ "libname": libname,
+ "deployVersion": deployVersion,
+ }).Debug("js lib not configured.")
+ }
+ return ""
+}
+
+func compareVersion(a, b *version) int {
+ if a.major > b.major {
+ return 1
+ }
+ if a.major < b.major {
+ return -1
+ }
+
+ if a.minor > b.minor {
+ return 1
+ }
+ if a.minor < b.minor {
+ return -1
+ }
+
+ if a.patch > b.patch {
+ return 1
+ }
+ if a.patch < b.patch {
+ return -1
+ }
+ return 0
+}
+
+func checkJSLib() {
+ for lib, vers := range V8JSLibs {
+ for _, ver := range vers {
+ p := filepath.Join("lib", ver, lib)
+ fi, err := os.Stat(p)
+ if os.IsNotExist(err) {
+ logging.VLog().WithFields(logrus.Fields{
+ "path": p,
+ }).Fatal("lib file not exist.")
+ }
+ if fi.IsDir() {
+ logging.VLog().WithFields(logrus.Fields{
+ "path": p,
+ }).Fatal("directory already exists with the same name.")
+ }
+
+ logging.VLog().WithFields(logrus.Fields{
+ "path": p,
+ }).Debug("check js lib.")
+ }
+ }
+}
+
+func parseVersion(ver string) (*version, error) {
+ ss := strings.Split(ver, ".")
+ if len(ss) != 3 {
+ return nil, ErrInvalidJSLibVersion
+ }
+
+ major, err := strconv.Atoi(ss[0])
+ if err != nil {
+ return nil, err
+ }
+
+ minor, err := strconv.Atoi(ss[1])
+ if err != nil {
+ return nil, err
+ }
+
+ patch, err := strconv.Atoi(ss[2])
+ if err != nil {
+ return nil, err
+ }
+ return &version{major, minor, patch}, nil
+}
+
+func (v *version) String() string {
+ return strings.Join([]string{
+ strconv.Itoa(v.major),
+ strconv.Itoa(v.minor),
+ strconv.Itoa(v.patch),
+ }, ".")
+}
+
+// convert V8JSLibs from string to type `version`
+func init() {
+ for lib, vers := range V8JSLibs {
+ for _, ver := range vers {
+ v, err := parseVersion(ver)
+ if err != nil {
+ logging.VLog().WithFields(logrus.Fields{
+ "err": err,
+ "lib": lib,
+ "version": ver,
+ }).Fatal("parse js lib version error.")
+ }
+
+ if _, ok := digitalized[lib]; !ok {
+ digitalized[lib] = make([]*version, 0)
+ }
+ digitalized[lib] = append(digitalized[lib], v)
+ }
+ }
+}
+
+// GetMaxV8JSLibVersionAtHeight ..
+func GetMaxV8JSLibVersionAtHeight(blockHeight uint64) string {
+ // V8JSLibVersionHeightSlice is already sorted at SetCompatibilityOptions func
+ for _, v := range V8JSLibVersionHeightSlice {
+ if blockHeight >= v.height {
+ return v.version
+ }
+ }
+ return ""
}
diff --git a/core/compatibility_test.go b/core/compatibility_test.go
new file mode 100644
index 000000000..0f5f31a4f
--- /dev/null
+++ b/core/compatibility_test.go
@@ -0,0 +1,82 @@
+// Copyright (C) 2018 go-nebulas authors
+//
+// This file is part of the go-nebulas library.
+//
+// the go-nebulas library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU 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-nebulas 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with the go-nebulas library. If not, see .
+//
+
+package core
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestStringCompare(t *testing.T) {
+ type test struct {
+ name, a, b string
+ cmp int
+ }
+
+ tests := []test{
+ {
+ name: "1",
+ a: "1.0.1",
+ b: "1.0.2",
+ cmp: -1,
+ },
+ {
+ name: "2",
+ a: "1.0.1",
+ b: "1.0.10",
+ cmp: -1,
+ },
+ {
+ name: "3",
+ a: "1.0.10",
+ b: "1.0.2",
+ cmp: 1,
+ },
+ {
+ name: "4",
+ a: "10.0.1",
+ b: "1.0.2",
+ cmp: 1,
+ },
+ {
+ name: "5",
+ a: "10.0.1",
+ b: "2.0.2",
+ cmp: 1,
+ },
+ {
+ name: "6",
+ a: "10.0.1",
+ b: "10.90.1",
+ cmp: -1,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ va, _ := parseVersion(tt.a)
+ vb, _ := parseVersion(tt.b)
+ r := compareVersion(va, vb)
+ assert.Equal(t, tt.a, va.String())
+ assert.Equal(t, tt.b, vb.String())
+ assert.Equal(t, tt.cmp, r, "case "+tt.name+" not equal")
+ })
+ }
+}
diff --git a/core/pb/block.pb.go b/core/pb/block.pb.go
index f6d575257..7856217e4 100644
--- a/core/pb/block.pb.go
+++ b/core/pb/block.pb.go
@@ -9,6 +9,7 @@ It is generated from these files:
It has these top-level messages:
Account
+ ContractMeta
Data
Transaction
BlockHeader
@@ -38,11 +39,12 @@ var _ = math.Inf
const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package
type Account struct {
- Address []byte `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"`
- Balance []byte `protobuf:"bytes,2,opt,name=balance,proto3" json:"balance,omitempty"`
- Nonce uint64 `protobuf:"varint,3,opt,name=nonce,proto3" json:"nonce,omitempty"`
- VarsHash []byte `protobuf:"bytes,4,opt,name=vars_hash,json=varsHash,proto3" json:"vars_hash,omitempty"`
- BirthPlace []byte `protobuf:"bytes,5,opt,name=birth_place,json=birthPlace,proto3" json:"birth_place,omitempty"`
+ Address []byte `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"`
+ Balance []byte `protobuf:"bytes,2,opt,name=balance,proto3" json:"balance,omitempty"`
+ Nonce uint64 `protobuf:"varint,3,opt,name=nonce,proto3" json:"nonce,omitempty"`
+ VarsHash []byte `protobuf:"bytes,4,opt,name=vars_hash,json=varsHash,proto3" json:"vars_hash,omitempty"`
+ BirthPlace []byte `protobuf:"bytes,5,opt,name=birth_place,json=birthPlace,proto3" json:"birth_place,omitempty"`
+ ContractMeta *ContractMeta `protobuf:"bytes,6,opt,name=contract_meta,json=contractMeta" json:"contract_meta,omitempty"`
}
func (m *Account) Reset() { *m = Account{} }
@@ -85,6 +87,29 @@ func (m *Account) GetBirthPlace() []byte {
return nil
}
+func (m *Account) GetContractMeta() *ContractMeta {
+ if m != nil {
+ return m.ContractMeta
+ }
+ return nil
+}
+
+type ContractMeta struct {
+ Version string `protobuf:"bytes,1,opt,name=version,proto3" json:"version,omitempty"`
+}
+
+func (m *ContractMeta) Reset() { *m = ContractMeta{} }
+func (m *ContractMeta) String() string { return proto.CompactTextString(m) }
+func (*ContractMeta) ProtoMessage() {}
+func (*ContractMeta) Descriptor() ([]byte, []int) { return fileDescriptorBlock, []int{1} }
+
+func (m *ContractMeta) GetVersion() string {
+ if m != nil {
+ return m.Version
+ }
+ return ""
+}
+
type Data struct {
Type string `protobuf:"bytes,1,opt,name=type,proto3" json:"type,omitempty"`
Payload []byte `protobuf:"bytes,2,opt,name=payload,proto3" json:"payload,omitempty"`
@@ -93,7 +118,7 @@ type Data struct {
func (m *Data) Reset() { *m = Data{} }
func (m *Data) String() string { return proto.CompactTextString(m) }
func (*Data) ProtoMessage() {}
-func (*Data) Descriptor() ([]byte, []int) { return fileDescriptorBlock, []int{1} }
+func (*Data) Descriptor() ([]byte, []int) { return fileDescriptorBlock, []int{2} }
func (m *Data) GetType() string {
if m != nil {
@@ -127,7 +152,7 @@ type Transaction struct {
func (m *Transaction) Reset() { *m = Transaction{} }
func (m *Transaction) String() string { return proto.CompactTextString(m) }
func (*Transaction) ProtoMessage() {}
-func (*Transaction) Descriptor() ([]byte, []int) { return fileDescriptorBlock, []int{2} }
+func (*Transaction) Descriptor() ([]byte, []int) { return fileDescriptorBlock, []int{3} }
func (m *Transaction) GetHash() []byte {
if m != nil {
@@ -231,7 +256,7 @@ type BlockHeader struct {
func (m *BlockHeader) Reset() { *m = BlockHeader{} }
func (m *BlockHeader) String() string { return proto.CompactTextString(m) }
func (*BlockHeader) ProtoMessage() {}
-func (*BlockHeader) Descriptor() ([]byte, []int) { return fileDescriptorBlock, []int{3} }
+func (*BlockHeader) Descriptor() ([]byte, []int) { return fileDescriptorBlock, []int{4} }
func (m *BlockHeader) GetHash() []byte {
if m != nil {
@@ -327,7 +352,7 @@ type Block struct {
func (m *Block) Reset() { *m = Block{} }
func (m *Block) String() string { return proto.CompactTextString(m) }
func (*Block) ProtoMessage() {}
-func (*Block) Descriptor() ([]byte, []int) { return fileDescriptorBlock, []int{4} }
+func (*Block) Descriptor() ([]byte, []int) { return fileDescriptorBlock, []int{5} }
func (m *Block) GetHeader() *BlockHeader {
if m != nil {
@@ -366,7 +391,7 @@ type NetBlocks struct {
func (m *NetBlocks) Reset() { *m = NetBlocks{} }
func (m *NetBlocks) String() string { return proto.CompactTextString(m) }
func (*NetBlocks) ProtoMessage() {}
-func (*NetBlocks) Descriptor() ([]byte, []int) { return fileDescriptorBlock, []int{5} }
+func (*NetBlocks) Descriptor() ([]byte, []int) { return fileDescriptorBlock, []int{6} }
func (m *NetBlocks) GetFrom() string {
if m != nil {
@@ -398,7 +423,7 @@ type NetBlock struct {
func (m *NetBlock) Reset() { *m = NetBlock{} }
func (m *NetBlock) String() string { return proto.CompactTextString(m) }
func (*NetBlock) ProtoMessage() {}
-func (*NetBlock) Descriptor() ([]byte, []int) { return fileDescriptorBlock, []int{6} }
+func (*NetBlock) Descriptor() ([]byte, []int) { return fileDescriptorBlock, []int{7} }
func (m *NetBlock) GetFrom() string {
if m != nil {
@@ -429,7 +454,7 @@ type DownloadBlock struct {
func (m *DownloadBlock) Reset() { *m = DownloadBlock{} }
func (m *DownloadBlock) String() string { return proto.CompactTextString(m) }
func (*DownloadBlock) ProtoMessage() {}
-func (*DownloadBlock) Descriptor() ([]byte, []int) { return fileDescriptorBlock, []int{7} }
+func (*DownloadBlock) Descriptor() ([]byte, []int) { return fileDescriptorBlock, []int{8} }
func (m *DownloadBlock) GetHash() []byte {
if m != nil {
@@ -453,7 +478,7 @@ type Random struct {
func (m *Random) Reset() { *m = Random{} }
func (m *Random) String() string { return proto.CompactTextString(m) }
func (*Random) ProtoMessage() {}
-func (*Random) Descriptor() ([]byte, []int) { return fileDescriptorBlock, []int{8} }
+func (*Random) Descriptor() ([]byte, []int) { return fileDescriptorBlock, []int{9} }
func (m *Random) GetVrfSeed() []byte {
if m != nil {
@@ -471,6 +496,7 @@ func (m *Random) GetVrfProof() []byte {
func init() {
proto.RegisterType((*Account)(nil), "corepb.Account")
+ proto.RegisterType((*ContractMeta)(nil), "corepb.ContractMeta")
proto.RegisterType((*Data)(nil), "corepb.Data")
proto.RegisterType((*Transaction)(nil), "corepb.Transaction")
proto.RegisterType((*BlockHeader)(nil), "corepb.BlockHeader")
@@ -484,53 +510,55 @@ func init() {
func init() { proto.RegisterFile("block.proto", fileDescriptorBlock) }
var fileDescriptorBlock = []byte{
- // 754 bytes of a gzipped FileDescriptorProto
- 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x54, 0x5f, 0x8b, 0xdb, 0x46,
- 0x10, 0x47, 0xb6, 0x2c, 0xdb, 0x23, 0xfb, 0x38, 0xb6, 0x47, 0x51, 0xdd, 0x96, 0x33, 0x2a, 0x2d,
- 0xa6, 0xa5, 0x36, 0x5c, 0x0b, 0xd7, 0xc7, 0x5e, 0x72, 0x0f, 0x97, 0x10, 0xc2, 0xb1, 0xc9, 0x4b,
- 0x20, 0x60, 0x56, 0xd2, 0x5a, 0x12, 0x91, 0x76, 0x85, 0x76, 0xed, 0xdc, 0x7d, 0x84, 0x3c, 0xe6,
- 0x7b, 0xe4, 0x25, 0xdf, 0x30, 0xec, 0xac, 0x64, 0xcb, 0x97, 0x83, 0x90, 0x27, 0xed, 0x6f, 0x7e,
- 0x3b, 0xa3, 0xf9, 0xcd, 0x9f, 0x05, 0x3f, 0x2a, 0x64, 0xfc, 0x6e, 0x59, 0xd5, 0x52, 0x4b, 0xe2,
- 0xc5, 0xb2, 0xe6, 0x55, 0x34, 0xbb, 0x4c, 0x73, 0x9d, 0x6d, 0xa3, 0x65, 0x2c, 0xcb, 0x95, 0xe0,
- 0xd1, 0xb6, 0x60, 0x2a, 0x97, 0xab, 0x54, 0xfe, 0xdd, 0x80, 0x55, 0x2c, 0xcb, 0x52, 0x8a, 0x55,
- 0xc2, 0xd2, 0x55, 0x15, 0x99, 0x8f, 0x0d, 0x30, 0xfb, 0xef, 0xdb, 0x8e, 0x42, 0x71, 0xa1, 0xb6,
- 0xca, 0xf8, 0x29, 0xcd, 0x34, 0xb7, 0x9e, 0xe1, 0x47, 0x07, 0x86, 0x57, 0x71, 0x2c, 0xb7, 0x42,
- 0x93, 0x00, 0x86, 0x2c, 0x49, 0x6a, 0xae, 0x54, 0xe0, 0xcc, 0x9d, 0xc5, 0x84, 0xb6, 0xd0, 0x30,
- 0x11, 0x2b, 0x98, 0x88, 0x79, 0xd0, 0xb3, 0x4c, 0x03, 0xc9, 0x19, 0x0c, 0x84, 0x34, 0xf6, 0xfe,
- 0xdc, 0x59, 0xb8, 0xd4, 0x02, 0xf2, 0x33, 0x8c, 0x77, 0xac, 0x56, 0xeb, 0x8c, 0xa9, 0x2c, 0x70,
- 0xd1, 0x63, 0x64, 0x0c, 0x37, 0x4c, 0x65, 0xe4, 0x1c, 0xfc, 0x28, 0xaf, 0x75, 0xb6, 0xae, 0x0a,
- 0x16, 0xf3, 0x60, 0x80, 0x34, 0xa0, 0xe9, 0xd6, 0x58, 0xc2, 0x7f, 0xc1, 0xbd, 0x66, 0x9a, 0x11,
- 0x02, 0xae, 0xbe, 0xaf, 0x38, 0x26, 0x33, 0xa6, 0x78, 0x36, 0x99, 0x54, 0xec, 0xbe, 0x90, 0x2c,
- 0x69, 0x33, 0x69, 0x60, 0xf8, 0xa9, 0x07, 0xfe, 0xeb, 0x9a, 0x09, 0xc5, 0x62, 0x9d, 0x4b, 0x61,
- 0xbc, 0xf1, 0xf7, 0x56, 0x0a, 0x9e, 0x8d, 0x6d, 0x53, 0xcb, 0xb2, 0x71, 0xc5, 0x33, 0x39, 0x81,
- 0x9e, 0x96, 0x98, 0xfe, 0x84, 0xf6, 0xb4, 0x34, 0x8a, 0x76, 0xac, 0xd8, 0xf2, 0x26, 0x6f, 0x0b,
- 0x0e, 0x3a, 0x07, 0x5d, 0x9d, 0xbf, 0xc0, 0x58, 0xe7, 0x25, 0x57, 0x9a, 0x95, 0x55, 0xe0, 0xcd,
- 0x9d, 0x45, 0x9f, 0x1e, 0x0c, 0x64, 0x0e, 0x6e, 0xc2, 0x34, 0x0b, 0x86, 0x73, 0x67, 0xe1, 0x5f,
- 0x4c, 0x96, 0xb6, 0xcb, 0x4b, 0xa3, 0x8d, 0x22, 0x43, 0x7e, 0x82, 0x51, 0x9c, 0xb1, 0x5c, 0xac,
- 0xf3, 0x24, 0x18, 0xcd, 0x9d, 0xc5, 0x94, 0x0e, 0x11, 0x3f, 0x4b, 0x4c, 0x09, 0x53, 0xa6, 0xd6,
- 0x55, 0x9d, 0xc7, 0x3c, 0x18, 0xdb, 0x12, 0xa6, 0x4c, 0xdd, 0x1a, 0xdc, 0x92, 0x45, 0x5e, 0xe6,
- 0x3a, 0x80, 0x3d, 0xf9, 0xc2, 0x60, 0x72, 0x0a, 0x7d, 0x56, 0xa4, 0x81, 0x8f, 0xf1, 0xcc, 0xd1,
- 0xc8, 0x56, 0x79, 0x2a, 0x82, 0x89, 0x95, 0x6d, 0xce, 0xe1, 0x87, 0x3e, 0xf8, 0x4f, 0xcc, 0x0c,
- 0xde, 0x70, 0x96, 0xf0, 0xfa, 0xd1, 0x72, 0x9d, 0x83, 0x5f, 0xb1, 0x9a, 0x0b, 0x6d, 0x1b, 0x69,
- 0xab, 0x06, 0xd6, 0x84, 0xad, 0x9c, 0xc1, 0x28, 0x96, 0xb9, 0x88, 0x98, 0x6a, 0xcb, 0xb5, 0xc7,
- 0xc7, 0xb5, 0x19, 0x3c, 0xac, 0x4d, 0x57, 0xb9, 0x77, 0xac, 0xbc, 0xc9, 0x7f, 0xf8, 0x75, 0xfe,
- 0xa3, 0x43, 0xfe, 0xe4, 0x57, 0x00, 0x9c, 0xe3, 0x75, 0x2d, 0xa5, 0x6e, 0x0a, 0x34, 0x46, 0x0b,
- 0x95, 0x52, 0x9b, 0xf8, 0xfa, 0x4e, 0x59, 0xd2, 0x16, 0x68, 0xa8, 0xef, 0x14, 0x52, 0xe7, 0xe0,
- 0xf3, 0x1d, 0x17, 0xba, 0x61, 0x7d, 0xab, 0xca, 0x9a, 0xf0, 0xc2, 0x15, 0x9c, 0xec, 0xf7, 0xc5,
- 0xde, 0x99, 0x60, 0x07, 0x67, 0xcb, 0xbd, 0xb9, 0x8a, 0x96, 0x4f, 0xdb, 0xb3, 0xf1, 0xa1, 0xd3,
- 0xb8, 0x0b, 0xc9, 0x1f, 0xe0, 0xd5, 0x4c, 0x24, 0xb2, 0x0c, 0xa6, 0xe8, 0x7a, 0xd2, 0x36, 0x9f,
- 0xa2, 0x95, 0x36, 0xec, 0x73, 0x77, 0xd4, 0x3f, 0x75, 0xc3, 0xcf, 0x0e, 0x0c, 0xb0, 0x17, 0xe4,
- 0x2f, 0xf0, 0x32, 0xec, 0x07, 0xf6, 0xc1, 0xbf, 0xf8, 0xa1, 0xf5, 0xeb, 0xb4, 0x8a, 0x36, 0x57,
- 0xc8, 0x25, 0x4c, 0xf4, 0x61, 0xe0, 0x55, 0xd0, 0x9b, 0xf7, 0xbb, 0x2e, 0x9d, 0x65, 0xa0, 0x47,
- 0x17, 0xc9, 0x9f, 0x00, 0x09, 0xaf, 0xb8, 0x48, 0xb8, 0x88, 0xef, 0x71, 0xf4, 0xfd, 0x0b, 0x58,
- 0x26, 0x2c, 0xc5, 0xe9, 0x4c, 0x69, 0x87, 0x25, 0x3f, 0x9a, 0x8c, 0xf2, 0x34, 0xd3, 0xd8, 0x60,
- 0x97, 0x36, 0x28, 0x7c, 0x0b, 0xe3, 0x97, 0x5c, 0x63, 0x5a, 0x6a, 0xbf, 0x57, 0xcd, 0xa6, 0xe2,
- 0x5e, 0x9d, 0xc1, 0x20, 0x62, 0x3a, 0xb6, 0x63, 0xe3, 0x52, 0x0b, 0xc8, 0xef, 0xe0, 0xe1, 0xcb,
- 0xa7, 0x82, 0x3e, 0x66, 0x3b, 0x3d, 0x12, 0x48, 0x1b, 0x32, 0x7c, 0x03, 0xa3, 0x36, 0xfa, 0x77,
- 0x04, 0xff, 0x0d, 0x06, 0xe8, 0xdf, 0x48, 0x7a, 0x10, 0xdb, 0x72, 0xe1, 0x25, 0x4c, 0xaf, 0xe5,
- 0x7b, 0x61, 0xde, 0x8c, 0x7d, 0xfc, 0xc7, 0x1e, 0x0a, 0x9c, 0xb8, 0x5e, 0x67, 0x63, 0xfe, 0x07,
- 0xcf, 0x76, 0xcf, 0x0c, 0xd7, 0xae, 0xde, 0xac, 0x15, 0xe7, 0x49, 0xfb, 0x52, 0xee, 0xea, 0xcd,
- 0x2b, 0xce, 0x71, 0x6d, 0x0d, 0x55, 0xd5, 0x52, 0x6e, 0x1a, 0x6f, 0x73, 0xf7, 0xd6, 0xe0, 0xc8,
- 0xc3, 0x37, 0xf7, 0x9f, 0x2f, 0x01, 0x00, 0x00, 0xff, 0xff, 0xb1, 0x18, 0x56, 0x0a, 0xfd, 0x05,
- 0x00, 0x00,
+ // 791 bytes of a gzipped FileDescriptorProto
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x54, 0xdd, 0x8a, 0x2b, 0x45,
+ 0x10, 0x26, 0xc9, 0xe4, 0xaf, 0x26, 0x59, 0x0e, 0xed, 0x22, 0xe3, 0xaa, 0x6c, 0x18, 0x51, 0x82,
+ 0x62, 0x02, 0xab, 0xb0, 0x7a, 0xe7, 0xd1, 0x73, 0x71, 0x14, 0x95, 0xa5, 0xf5, 0x46, 0x10, 0x42,
+ 0x4d, 0x4f, 0xef, 0x64, 0x30, 0xd3, 0x3d, 0x74, 0x77, 0xe2, 0xd9, 0x47, 0xf0, 0x5d, 0xbc, 0xf1,
+ 0x3d, 0x7c, 0x28, 0xe9, 0xea, 0x9e, 0xec, 0xec, 0xf1, 0x80, 0x78, 0x35, 0xfd, 0xd5, 0x37, 0x5f,
+ 0x53, 0xf5, 0x55, 0x55, 0x43, 0x5a, 0x1c, 0xb4, 0xf8, 0x6d, 0xd3, 0x1a, 0xed, 0x34, 0x9b, 0x08,
+ 0x6d, 0x64, 0x5b, 0x5c, 0xdd, 0x56, 0xb5, 0xdb, 0x1f, 0x8b, 0x8d, 0xd0, 0xcd, 0x56, 0xc9, 0xe2,
+ 0x78, 0x40, 0x5b, 0xeb, 0x6d, 0xa5, 0x3f, 0x8d, 0x60, 0x2b, 0x74, 0xd3, 0x68, 0xb5, 0x2d, 0xb1,
+ 0xda, 0xb6, 0x85, 0xff, 0x84, 0x0b, 0xae, 0xbe, 0xf8, 0x6f, 0xa1, 0xb2, 0x52, 0xd9, 0xa3, 0xf5,
+ 0x3a, 0xeb, 0xd0, 0xc9, 0xa0, 0xcc, 0xff, 0x1e, 0xc0, 0xf4, 0xb9, 0x10, 0xfa, 0xa8, 0x1c, 0xcb,
+ 0x60, 0x8a, 0x65, 0x69, 0xa4, 0xb5, 0xd9, 0x60, 0x35, 0x58, 0x2f, 0x78, 0x07, 0x3d, 0x53, 0xe0,
+ 0x01, 0x95, 0x90, 0xd9, 0x30, 0x30, 0x11, 0xb2, 0x4b, 0x18, 0x2b, 0xed, 0xe3, 0xa3, 0xd5, 0x60,
+ 0x9d, 0xf0, 0x00, 0xd8, 0xbb, 0x30, 0x3f, 0xa1, 0xb1, 0xbb, 0x3d, 0xda, 0x7d, 0x96, 0x90, 0x62,
+ 0xe6, 0x03, 0x2f, 0xd1, 0xee, 0xd9, 0x35, 0xa4, 0x45, 0x6d, 0xdc, 0x7e, 0xd7, 0x1e, 0x50, 0xc8,
+ 0x6c, 0x4c, 0x34, 0x50, 0xe8, 0xce, 0x47, 0xd8, 0x97, 0xb0, 0x14, 0x5a, 0x39, 0x83, 0xc2, 0xed,
+ 0x1a, 0xe9, 0x30, 0x9b, 0xac, 0x06, 0xeb, 0xf4, 0xe6, 0x72, 0x13, 0x6c, 0xda, 0x7c, 0x13, 0xc9,
+ 0x1f, 0xa4, 0x43, 0xbe, 0x10, 0x3d, 0x94, 0xaf, 0x61, 0xd1, 0x67, 0x7d, 0xe2, 0x27, 0x69, 0x6c,
+ 0xad, 0x15, 0x95, 0x34, 0xe7, 0x1d, 0xcc, 0x3f, 0x87, 0xe4, 0x05, 0x3a, 0x64, 0x0c, 0x12, 0xf7,
+ 0xd0, 0xca, 0x48, 0xd3, 0xd9, 0xab, 0x5a, 0x7c, 0x38, 0x68, 0x2c, 0xbb, 0x72, 0x23, 0xcc, 0xff,
+ 0x1c, 0x42, 0xfa, 0xb3, 0x41, 0x65, 0x51, 0xb8, 0x5a, 0x2b, 0xaf, 0xa6, 0x1a, 0x83, 0x5f, 0x74,
+ 0xf6, 0xb1, 0x7b, 0xa3, 0x9b, 0x28, 0xa5, 0x33, 0xbb, 0x80, 0xa1, 0xd3, 0xe4, 0xd1, 0x82, 0x0f,
+ 0x9d, 0xf6, 0xb6, 0x9d, 0xf0, 0x70, 0x94, 0xd1, 0x9c, 0x00, 0x1e, 0xcd, 0x1c, 0xf7, 0xcd, 0x7c,
+ 0x0f, 0xe6, 0xae, 0x6e, 0xa4, 0x75, 0xd8, 0xb4, 0x64, 0xc5, 0x88, 0x3f, 0x06, 0xd8, 0x0a, 0x92,
+ 0x12, 0x1d, 0x66, 0x53, 0xf2, 0x68, 0xd1, 0x79, 0xe4, 0x6b, 0xe3, 0xc4, 0xb0, 0x77, 0x60, 0x26,
+ 0xf6, 0x58, 0xab, 0x5d, 0x5d, 0x66, 0xb3, 0xd5, 0x60, 0xbd, 0xe4, 0x53, 0xc2, 0xdf, 0x96, 0xbe,
+ 0x4f, 0x15, 0xda, 0x5d, 0x6b, 0x6a, 0x21, 0xb3, 0x79, 0xe8, 0x53, 0x85, 0xf6, 0xce, 0xe3, 0x8e,
+ 0x3c, 0xd4, 0x4d, 0xed, 0x32, 0x38, 0x93, 0xdf, 0x7b, 0xcc, 0x9e, 0xc1, 0x08, 0x0f, 0x55, 0x96,
+ 0xd2, 0x7d, 0xfe, 0xe8, 0xcb, 0xb6, 0x75, 0xa5, 0xb2, 0x45, 0x28, 0xdb, 0x9f, 0xf3, 0x3f, 0x46,
+ 0x90, 0x7e, 0xed, 0x07, 0xfd, 0xa5, 0xc4, 0x52, 0x9a, 0x37, 0xda, 0x75, 0x0d, 0x69, 0x8b, 0x46,
+ 0x2a, 0x17, 0xa6, 0x25, 0xb8, 0x06, 0x21, 0x44, 0xf3, 0x72, 0x05, 0x33, 0xa1, 0x6b, 0x55, 0xa0,
+ 0xed, 0xec, 0x3a, 0xe3, 0xa7, 0xde, 0x8c, 0x5f, 0xf7, 0xa6, 0x5f, 0xf9, 0xe4, 0x69, 0xe5, 0x31,
+ 0xff, 0xe9, 0xbf, 0xf3, 0x9f, 0x3d, 0xe6, 0xcf, 0xde, 0x07, 0xa0, 0x65, 0xd9, 0x19, 0xad, 0x5d,
+ 0x34, 0x68, 0x4e, 0x11, 0xae, 0xb5, 0xf3, 0xf7, 0xbb, 0x57, 0x36, 0x90, 0xc1, 0xa0, 0xa9, 0x7b,
+ 0x65, 0x89, 0xba, 0x86, 0x54, 0x9e, 0xa4, 0x72, 0x91, 0x4d, 0x43, 0x55, 0x21, 0x44, 0x3f, 0x3c,
+ 0x87, 0x8b, 0xf3, 0x52, 0x86, 0x7f, 0x16, 0xd4, 0xc1, 0xab, 0xcd, 0x39, 0x1c, 0x46, 0x3d, 0x9c,
+ 0xbd, 0x86, 0x2f, 0x45, 0x1f, 0xb2, 0x8f, 0x60, 0x62, 0x50, 0x95, 0xba, 0xc9, 0x96, 0x24, 0xbd,
+ 0xe8, 0x9a, 0xcf, 0x29, 0xca, 0x23, 0xfb, 0x5d, 0x32, 0x1b, 0x3d, 0x4b, 0xf2, 0xbf, 0x06, 0x30,
+ 0xa6, 0x5e, 0xb0, 0x4f, 0x60, 0xb2, 0xa7, 0x7e, 0x50, 0x1f, 0xd2, 0x9b, 0xb7, 0x3a, 0x5d, 0xaf,
+ 0x55, 0x3c, 0xfe, 0xc2, 0x6e, 0x61, 0xe1, 0x1e, 0x07, 0xde, 0x66, 0xc3, 0xd5, 0xa8, 0x2f, 0xe9,
+ 0x2d, 0x03, 0x7f, 0xf2, 0x23, 0xfb, 0x18, 0xa0, 0x94, 0xad, 0x54, 0xa5, 0x54, 0xe2, 0x81, 0x46,
+ 0x3f, 0xbd, 0x81, 0x4d, 0x89, 0x15, 0x4d, 0x67, 0xc5, 0x7b, 0x2c, 0x7b, 0xdb, 0x67, 0x54, 0x57,
+ 0x7b, 0x47, 0x0d, 0x4e, 0x78, 0x44, 0xf9, 0xaf, 0x30, 0xff, 0x51, 0x3a, 0x4a, 0xcb, 0x9e, 0xf7,
+ 0x2a, 0x6e, 0x2a, 0xed, 0xd5, 0x25, 0x8c, 0x0b, 0x74, 0x22, 0x8c, 0x4d, 0xc2, 0x03, 0x60, 0x1f,
+ 0xc2, 0x84, 0x9e, 0x57, 0x9b, 0x8d, 0x28, 0xdb, 0xe5, 0x93, 0x02, 0x79, 0x24, 0xf3, 0x5f, 0x60,
+ 0xd6, 0xdd, 0xfe, 0x3f, 0x2e, 0xff, 0x00, 0xc6, 0xa4, 0x8f, 0x25, 0xbd, 0x76, 0x77, 0xe0, 0xf2,
+ 0x5b, 0x58, 0xbe, 0xd0, 0xbf, 0x2b, 0xff, 0x66, 0x9c, 0xef, 0x7f, 0xd3, 0x43, 0x41, 0x13, 0x37,
+ 0xec, 0x6d, 0xcc, 0x57, 0x30, 0x09, 0xdd, 0xf3, 0xc3, 0x75, 0x32, 0xf7, 0x3b, 0x2b, 0x65, 0xd9,
+ 0x3d, 0xc7, 0x27, 0x73, 0xff, 0x93, 0x94, 0xb4, 0xb6, 0x9e, 0x6a, 0x8d, 0xd6, 0xf7, 0x51, 0xed,
+ 0xff, 0xbd, 0xf3, 0xb8, 0x98, 0xd0, 0xc3, 0xfe, 0xd9, 0x3f, 0x01, 0x00, 0x00, 0xff, 0xff, 0xa9,
+ 0xc7, 0x49, 0xf3, 0x62, 0x06, 0x00, 0x00,
}
diff --git a/core/pb/block.proto b/core/pb/block.proto
index 9e1f0f714..df4aafa7d 100644
--- a/core/pb/block.proto
+++ b/core/pb/block.proto
@@ -28,6 +28,11 @@ message Account {
uint64 nonce = 3;
bytes vars_hash = 4;
bytes birth_place = 5;
+ ContractMeta contract_meta = 6;
+}
+
+message ContractMeta {
+ string version = 1;
}
message Data {
diff --git a/core/state/account_state.go b/core/state/account_state.go
index fed37295b..1810c3015 100644
--- a/core/state/account_state.go
+++ b/core/state/account_state.go
@@ -47,6 +47,8 @@ type account struct {
variables *trie.Trie
// ContractType: Transaction Hash
birthPlace byteutils.Hash
+
+ contractMeta *corepb.ContractMeta
}
// ToBytes converts domain Account to bytes
@@ -56,11 +58,12 @@ func (acc *account) ToBytes() ([]byte, error) {
return nil, err
}
pbAcc := &corepb.Account{
- Address: acc.address,
- Balance: value,
- Nonce: acc.nonce,
- VarsHash: acc.variables.RootHash(),
- BirthPlace: acc.birthPlace,
+ Address: acc.address,
+ Balance: value,
+ Nonce: acc.nonce,
+ VarsHash: acc.variables.RootHash(),
+ BirthPlace: acc.birthPlace,
+ ContractMeta: acc.contractMeta,
}
bytes, err := proto.Marshal(pbAcc)
if err != nil {
@@ -83,6 +86,7 @@ func (acc *account) FromBytes(bytes []byte, storage storage.Storage) error {
acc.balance = value
acc.nonce = pbAcc.Nonce
acc.birthPlace = pbAcc.BirthPlace
+ acc.contractMeta = pbAcc.ContractMeta
acc.variables, err = trie.NewTrie(pbAcc.VarsHash, storage, false)
if err != nil {
return err
@@ -115,6 +119,11 @@ func (acc *account) BirthPlace() byteutils.Hash {
return acc.birthPlace
}
+// ContractMeta ..
+func (acc *account) ContractMeta() *corepb.ContractMeta {
+ return acc.contractMeta
+}
+
// Clone account
func (acc *account) Clone() (Account, error) {
variables, err := acc.variables.Clone()
@@ -123,11 +132,12 @@ func (acc *account) Clone() (Account, error) {
}
return &account{
- address: acc.address,
- balance: acc.balance,
- nonce: acc.nonce,
- variables: variables,
- birthPlace: acc.birthPlace,
+ address: acc.address,
+ balance: acc.balance,
+ nonce: acc.nonce,
+ variables: variables,
+ birthPlace: acc.birthPlace,
+ contractMeta: acc.contractMeta, // TODO: Clone() ?
}, nil
}
@@ -184,13 +194,14 @@ func (acc *account) Iterator(prefix []byte) (Iterator, error) {
}
func (acc *account) String() string {
- return fmt.Sprintf("Account %p {Address: %v, Balance:%v; Nonce:%v; VarsHash:%v; BirthPlace:%v}",
+ return fmt.Sprintf("Account %p {Address: %v, Balance:%v; Nonce:%v; VarsHash:%v; BirthPlace:%v; ContractMeta:%v}",
acc,
byteutils.Hex(acc.address),
acc.balance,
acc.nonce,
byteutils.Hex(acc.variables.RootHash()),
acc.birthPlace.Hex(),
+ acc.contractMeta.String(), // TODO: check nil?
)
}
@@ -219,17 +230,18 @@ func (as *accountState) recordDirtyAccount(addr byteutils.Hash, acc Account) {
as.dirtyAccount[addr.Hex()] = acc
}
-func (as *accountState) newAccount(addr byteutils.Hash, birthPlace byteutils.Hash) (Account, error) {
+func (as *accountState) newAccount(addr byteutils.Hash, birthPlace byteutils.Hash, contractMeta *corepb.ContractMeta) (Account, error) {
varTrie, err := trie.NewTrie(nil, as.storage, false)
if err != nil {
return nil, err
}
acc := &account{
- address: addr,
- balance: util.NewUint128(),
- nonce: 0,
- variables: varTrie,
- birthPlace: birthPlace,
+ address: addr,
+ balance: util.NewUint128(),
+ nonce: 0,
+ variables: varTrie,
+ birthPlace: birthPlace,
+ contractMeta: contractMeta,
}
as.recordDirtyAccount(addr, acc)
return acc, nil
@@ -290,7 +302,7 @@ func (as *accountState) GetOrCreateUserAccount(addr byteutils.Hash) (Account, er
return nil, err
}
if err == ErrAccountNotFound {
- acc, err = as.newAccount(addr, nil)
+ acc, err = as.newAccount(addr, nil, nil)
if err != nil {
return nil, err
}
@@ -314,8 +326,8 @@ func (as *accountState) GetContractAccount(addr byteutils.Hash) (Account, error)
}
// CreateContractAccount according to the addr, and set birthPlace as creation tx hash
-func (as *accountState) CreateContractAccount(addr byteutils.Hash, birthPlace byteutils.Hash) (Account, error) {
- return as.newAccount(addr, birthPlace)
+func (as *accountState) CreateContractAccount(addr byteutils.Hash, birthPlace byteutils.Hash, contractMeta *corepb.ContractMeta) (Account, error) {
+ return as.newAccount(addr, birthPlace, contractMeta)
}
func (as *accountState) Accounts() ([]Account, error) { // TODO delete
@@ -394,3 +406,12 @@ func (as *accountState) String() string {
as.storage,
)
}
+
+// MockAccount nf/nvm/engine.CheckV8Run() & cmd/v8/main.go
+func MockAccount(version string) Account {
+ return &account{
+ contractMeta: &corepb.ContractMeta{
+ Version: version,
+ },
+ }
+}
diff --git a/core/state/types.go b/core/state/types.go
index 92720a90c..4900b3346 100644
--- a/core/state/types.go
+++ b/core/state/types.go
@@ -22,6 +22,7 @@ import (
"errors"
"github.com/nebulasio/go-nebulas/consensus/pb"
+ "github.com/nebulasio/go-nebulas/core/pb"
"github.com/nebulasio/go-nebulas/storage"
"github.com/nebulasio/go-nebulas/util"
"github.com/nebulasio/go-nebulas/util/byteutils"
@@ -67,6 +68,7 @@ type Account interface {
Get(key []byte) ([]byte, error)
Del(key []byte) error
Iterator(prefix []byte) (Iterator, error)
+ ContractMeta() *corepb.ContractMeta
}
// AccountState Interface
@@ -84,7 +86,7 @@ type AccountState interface {
GetOrCreateUserAccount(byteutils.Hash) (Account, error)
GetContractAccount(byteutils.Hash) (Account, error)
- CreateContractAccount(byteutils.Hash, byteutils.Hash) (Account, error)
+ CreateContractAccount(byteutils.Hash, byteutils.Hash, *corepb.ContractMeta) (Account, error)
}
// Event event structure.
@@ -142,7 +144,7 @@ type WorldState interface {
Accounts() ([]Account, error)
GetOrCreateUserAccount(addr byteutils.Hash) (Account, error)
GetContractAccount(addr byteutils.Hash) (Account, error)
- CreateContractAccount(owner byteutils.Hash, birthPlace byteutils.Hash) (Account, error)
+ CreateContractAccount(owner byteutils.Hash, birthPlace byteutils.Hash, contractMeta *corepb.ContractMeta) (Account, error)
GetTx(txHash byteutils.Hash) ([]byte, error)
PutTx(txHash byteutils.Hash, txBytes []byte) error
@@ -155,6 +157,8 @@ type WorldState interface {
RecordGas(from string, gas *util.Uint128) error
GetGas() map[string]*util.Uint128
+ GetBlockHashByHeight(height uint64) ([]byte, error)
+ GetBlock(txHash byteutils.Hash) ([]byte, error)
}
// TxWorldState is the world state of a single transaction
@@ -171,7 +175,7 @@ type TxWorldState interface {
Accounts() ([]Account, error)
GetOrCreateUserAccount(addr byteutils.Hash) (Account, error)
GetContractAccount(addr byteutils.Hash) (Account, error)
- CreateContractAccount(owner byteutils.Hash, birthPlace byteutils.Hash) (Account, error)
+ CreateContractAccount(owner byteutils.Hash, birthPlace byteutils.Hash, contractMeta *corepb.ContractMeta) (Account, error)
GetTx(txHash byteutils.Hash) ([]byte, error)
PutTx(txHash byteutils.Hash, txBytes []byte) error
@@ -183,4 +187,6 @@ type TxWorldState interface {
DynastyRoot() byteutils.Hash
RecordGas(from string, gas *util.Uint128) error
+ GetBlockHashByHeight(height uint64) ([]byte, error)
+ GetBlock(txHash byteutils.Hash) ([]byte, error)
}
diff --git a/core/state/world_state.go b/core/state/world_state.go
index 71d756af0..cc1888f08 100644
--- a/core/state/world_state.go
+++ b/core/state/world_state.go
@@ -22,6 +22,7 @@ import (
"encoding/json"
"github.com/nebulasio/go-nebulas/consensus/pb"
+ "github.com/nebulasio/go-nebulas/core/pb"
"github.com/nebulasio/go-nebulas/util"
"github.com/nebulasio/go-nebulas/common/mvccdb"
@@ -411,8 +412,8 @@ func (s *states) GetContractAccount(addr byteutils.Hash) (Account, error) {
return s.recordAccount(acc)
}
-func (s *states) CreateContractAccount(owner byteutils.Hash, birthPlace byteutils.Hash) (Account, error) {
- acc, err := s.accState.CreateContractAccount(owner, birthPlace)
+func (s *states) CreateContractAccount(owner byteutils.Hash, birthPlace byteutils.Hash, contractMeta *corepb.ContractMeta) (Account, error) {
+ acc, err := s.accState.CreateContractAccount(owner, birthPlace, contractMeta)
if err != nil {
return nil, err
}
@@ -540,6 +541,22 @@ func (s *states) GetGas() map[string]*util.Uint128 {
return gasConsumed
}
+func (s *states) GetBlockHashByHeight(height uint64) ([]byte, error) {
+ bytes, err := s.innerDB.Get(byteutils.FromUint64(height))
+ if err != nil {
+ return nil, err
+ }
+ return bytes, nil
+}
+
+func (s *states) GetBlock(hash byteutils.Hash) ([]byte, error) {
+ bytes, err := s.innerDB.Get(hash)
+ if err != nil {
+ return nil, err
+ }
+ return bytes, nil
+}
+
// WorldState manange all current states in Blockchain
type worldState struct {
*states
diff --git a/core/transaction.go b/core/transaction.go
index 3f62d93ff..cdc555332 100644
--- a/core/transaction.go
+++ b/core/transaction.go
@@ -558,6 +558,9 @@ func VerifyExecution(tx *Transaction, block *Block, ws WorldState) (bool, error)
// step7. execute contract.
gasExecution, exeResult, exeErr := payload.Execute(contractLimitedGas, tx, block, ws)
+ if exeErr == ErrUnexpected {
+ return false, exeErr
+ }
// step8. calculate final gas.
allGas, gasErr := gasUsed.Add(gasExecution)
diff --git a/core/transaction_binary_payload.go b/core/transaction_binary_payload.go
index 28cb293b0..211ad713e 100644
--- a/core/transaction_binary_payload.go
+++ b/core/transaction_binary_payload.go
@@ -22,6 +22,8 @@ import (
"fmt"
"github.com/nebulasio/go-nebulas/util"
+ "github.com/nebulasio/go-nebulas/util/logging"
+ "github.com/sirupsen/logrus"
)
// BinaryPayload carry some data
@@ -90,11 +92,17 @@ func (payload *BinaryPayload) Execute(limitedGas *util.Uint128, tx *Transaction,
}
result, exeErr := engine.Call(deploy.Source, deploy.SourceType, ContractAcceptFunc, "")
- gasCout := engine.ExecutionInstructions()
- instructions, err := util.NewUint128FromInt(int64(gasCout))
- if err != nil {
- return util.NewUint128(), "", err
+ gasCount := engine.ExecutionInstructions()
+ instructions, err := util.NewUint128FromInt(int64(gasCount))
+ if err != nil || exeErr == ErrUnexpected {
+ logging.VLog().WithFields(logrus.Fields{
+ "err": err,
+ "exeErr": exeErr,
+ "gasCount": gasCount,
+ }).Error("Unexpected error when executing binary")
+ return util.NewUint128(), "", ErrUnexpected
}
+
if exeErr == ErrExecutionFailed && len(result) > 0 {
exeErr = fmt.Errorf("Binary: %s", result)
}
diff --git a/core/transaction_call_payload.go b/core/transaction_call_payload.go
index 978421b10..7300f4ad0 100644
--- a/core/transaction_call_payload.go
+++ b/core/transaction_call_payload.go
@@ -24,6 +24,8 @@ import (
"github.com/nebulasio/go-nebulas/util"
"github.com/nebulasio/go-nebulas/util/byteutils"
+ "github.com/nebulasio/go-nebulas/util/logging"
+ "github.com/sirupsen/logrus"
)
// CallPayload carry function call information
@@ -145,10 +147,16 @@ func (payload *CallPayload) Execute(limitedGas *util.Uint128, tx *Transaction, b
}
result, exeErr := engine.Call(deploy.Source, deploy.SourceType, payload.Function, payload.Args)
- gasCout := engine.ExecutionInstructions()
- instructions, err := util.NewUint128FromInt(int64(gasCout))
- if err != nil {
- return util.NewUint128(), "", err
+ gasCount := engine.ExecutionInstructions()
+ instructions, err := util.NewUint128FromInt(int64(gasCount))
+
+ if err != nil || exeErr == ErrUnexpected {
+ logging.VLog().WithFields(logrus.Fields{
+ "err": err,
+ "exeErr": exeErr,
+ "gasCount": gasCount,
+ }).Error("Unexpected error when executing call")
+ return util.NewUint128(), "", ErrUnexpected
}
if IsCompatibleStack(block.header.chainID, tx.hash) {
instructions = limitedGas
diff --git a/core/transaction_deploy_payload.go b/core/transaction_deploy_payload.go
index 58bb27bd2..52f7fd88a 100644
--- a/core/transaction_deploy_payload.go
+++ b/core/transaction_deploy_payload.go
@@ -22,6 +22,11 @@ import (
"encoding/json"
"fmt"
+ "github.com/nebulasio/go-nebulas/core/pb"
+ "github.com/nebulasio/go-nebulas/core/state"
+ "github.com/nebulasio/go-nebulas/util/logging"
+ "github.com/sirupsen/logrus"
+
"github.com/nebulasio/go-nebulas/util"
)
@@ -108,7 +113,13 @@ func (payload *DeployPayload) Execute(limitedGas *util.Uint128, tx *Transaction,
if err != nil {
return util.NewUint128(), "", err
} */
- contract, err := ws.CreateContractAccount(addr.Bytes(), tx.Hash())
+ var contract state.Account
+ v := GetMaxV8JSLibVersionAtHeight(block.Height())
+ if len(v) > 0 {
+ contract, err = ws.CreateContractAccount(addr.Bytes(), tx.Hash(), &corepb.ContractMeta{Version: v})
+ } else {
+ contract, err = ws.CreateContractAccount(addr.Bytes(), tx.Hash(), nil)
+ }
if err != nil {
return util.NewUint128(), "", err
}
@@ -125,10 +136,15 @@ func (payload *DeployPayload) Execute(limitedGas *util.Uint128, tx *Transaction,
// Deploy and Init.
result, exeErr := engine.DeployAndInit(payload.Source, payload.SourceType, payload.Args)
- gasCout := engine.ExecutionInstructions()
- instructions, err := util.NewUint128FromInt(int64(gasCout))
- if err != nil {
- return util.NewUint128(), "", err
+ gasCount := engine.ExecutionInstructions()
+ instructions, err := util.NewUint128FromInt(int64(gasCount))
+ if err != nil || exeErr == ErrUnexpected {
+ logging.VLog().WithFields(logrus.Fields{
+ "err": err,
+ "exeErr": exeErr,
+ "gasCount": gasCount,
+ }).Error("Unexpected error when executing deploy ")
+ return util.NewUint128(), "", ErrUnexpected
}
if exeErr != nil && exeErr == ErrExecutionFailed && len(result) > 0 {
exeErr = fmt.Errorf("Deploy: %s", result)
diff --git a/core/types.go b/core/types.go
index 392994f9a..cc50eefd6 100644
--- a/core/types.go
+++ b/core/types.go
@@ -170,6 +170,7 @@ var (
// nvm error
ErrExecutionFailed = errors.New("execution failed")
+ ErrUnexpected = errors.New("Unexpected sys error")
// unsupported keyword error in smart contract
ErrUnsupportedKeyword = errors.New("transaction data has unsupported keyword")
@@ -290,7 +291,7 @@ type Neblet interface {
type WorldState interface {
GetOrCreateUserAccount(addr byteutils.Hash) (state.Account, error)
GetContractAccount(addr byteutils.Hash) (state.Account, error)
- CreateContractAccount(owner byteutils.Hash, birthPlace byteutils.Hash) (state.Account, error)
+ CreateContractAccount(owner byteutils.Hash, birthPlace byteutils.Hash, contractMeta *corepb.ContractMeta) (state.Account, error)
GetTx(txHash byteutils.Hash) ([]byte, error)
PutTx(txHash byteutils.Hash, txBytes []byte) error
@@ -304,4 +305,6 @@ type WorldState interface {
RecordGas(from string, gas *util.Uint128) error
Reset(addr byteutils.Hash) error
+ GetBlockHashByHeight(height uint64) ([]byte, error)
+ GetBlock(txHash byteutils.Hash) ([]byte, error)
}
diff --git a/crypto/hash/hash.go b/crypto/hash/hash.go
index 355c8cafd..f5b7e1d55 100644
--- a/crypto/hash/hash.go
+++ b/crypto/hash/hash.go
@@ -20,12 +20,18 @@ package hash
import (
"crypto/sha256"
+ "encoding/base64"
keccak "github.com/nebulasio/go-nebulas/crypto/sha3"
"golang.org/x/crypto/ripemd160"
"golang.org/x/crypto/sha3"
)
+// const alphabet = "./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
+
+var bcEncoding = base64.NewEncoding(alphabet)
+
// Sha256 returns the SHA-256 digest of the data.
func Sha256(args ...[]byte) []byte {
hasher := sha256.New()
@@ -61,3 +67,29 @@ func Ripemd160(args ...[]byte) []byte {
}
return hasher.Sum(nil)
}
+
+// Base64Encode encode to base64
+func Base64Encode(src []byte) []byte {
+ n := bcEncoding.EncodedLen(len(src))
+ dst := make([]byte, n)
+ bcEncoding.Encode(dst, src)
+ // for dst[n-1] == '=' {
+ // n--
+ // }
+ return dst[:n]
+}
+
+// Base64Decode decode base64
+func Base64Decode(src []byte) ([]byte, error) {
+ numOfEquals := 4 - (len(src) % 4)
+ for i := 0; i < numOfEquals; i++ {
+ src = append(src, '=')
+ }
+
+ dst := make([]byte, bcEncoding.DecodedLen(len(src)))
+ n, err := bcEncoding.Decode(dst, src)
+ if err != nil {
+ return nil, err
+ }
+ return dst[:n], nil
+}
diff --git a/nebtestkit/cases/contract/contract.deploy.test.js b/nebtestkit/cases/contract/contract.deploy.test.js
index cb2ae6da2..c0037ffb9 100644
--- a/nebtestkit/cases/contract/contract.deploy.test.js
+++ b/nebtestkit/cases/contract/contract.deploy.test.js
@@ -1291,7 +1291,7 @@ describe('contract deploy', function () {
status: 0,
fromBalanceAfterTx: '9999999976573000000',
toBalanceAfterTx: '0',
- transferReward: '23437000000'
+ transferReward: '23427000000'
};
prepare((err) => {
if (err) {
diff --git a/nebtestkit/cases/contract/contract.feature.date.test.js b/nebtestkit/cases/contract/contract.feature.date.test.js
index 4fba6e3d6..4dd7375f6 100644
--- a/nebtestkit/cases/contract/contract.feature.date.test.js
+++ b/nebtestkit/cases/contract/contract.feature.date.test.js
@@ -345,7 +345,7 @@ var caseGroup = {
}
},
{
- "name": "0-3. test unsupported method",
+ "name": "0-3. test locale method",
"testInput": {
value: "0",
nonce: 1,
@@ -357,10 +357,9 @@ var caseGroup = {
}
},
"testExpect": {
- canExcuteTx: false,
+ canExcuteTx: true,
toBalanceChange: "0",
- status: 0,
- eventErr: "Call: Error: Unsupported method!"
+ status: 1
}
}
]
diff --git a/nebtestkit/cases/contract/contract.feature.random.test.js b/nebtestkit/cases/contract/contract.feature.random.test.js
index 9252ed3b8..4466f52a6 100644
--- a/nebtestkit/cases/contract/contract.feature.random.test.js
+++ b/nebtestkit/cases/contract/contract.feature.random.test.js
@@ -307,7 +307,7 @@ var caseGroup = {
cases: [
{
- "name": "0-1. test 'not define user seed'",
+ "name": "0-1. test 'undefined user seed'",
"testInput": {
value: "0",
nonce: 1,
@@ -326,7 +326,7 @@ var caseGroup = {
}
},
{
- "name": "0-2. test 'empty user seed('') == reset random seed'",
+ "name": "0-2. test 'empty user seed('')'",
"testInput": {
value: "0",
nonce: 1,
@@ -341,7 +341,7 @@ var caseGroup = {
canExcuteTx: true,
toBalanceChange: "0",
status: 1,
- equalr1r2: true
+ equalr1r2: false
}
},
{
diff --git a/nebtestkit/cases/contract/contract.features.test.js b/nebtestkit/cases/contract/contract.features.test.js
new file mode 100644
index 000000000..0b8d7f4a3
--- /dev/null
+++ b/nebtestkit/cases/contract/contract.features.test.js
@@ -0,0 +1,339 @@
+"use strict";
+
+var Wallet = require("nebulas");
+var HttpRequest = require("../../node-request.js");
+var TestNetConfig = require("../testnet_config.js");
+var Neb = Wallet.Neb;
+var Transaction = Wallet.Transaction;
+var FS = require("fs");
+var expect = require('chai').expect;
+var Unit = Wallet.Unit;
+
+// mocha cases/contract/xxx testneb2 -t 2000000
+var args = process.argv.splice(2);
+var env = args[1];
+if (env == null){
+ env = "local";
+}
+var testNetConfig = new TestNetConfig(env);
+
+var neb = new Neb();
+var ChainID = testNetConfig.ChainId;
+var sourceAccount = testNetConfig.sourceAccount;
+var coinbase = testNetConfig.coinbase;
+var apiEndPoint = testNetConfig.apiEndPoint;
+neb.setRequest(new HttpRequest(apiEndPoint));
+
+var contractAddress;
+var toAddress = Wallet.Account.NewAccount();
+var nonce;
+var contractNonce = 0;
+
+/*
+ * set this value according to the status of your testnet.
+ * the smaller the value, the faster the test, with the risk of causing error
+ */
+
+var maxCheckTime = 50;
+var checkTimes = 0;
+var beginCheckTime;
+
+console.log("env:", env);
+
+function checkTransaction(hash, callback) {
+ if (checkTimes === 0) {
+ beginCheckTime = new Date().getTime();
+ }
+ checkTimes += 1;
+ if (checkTimes > maxCheckTime) {
+ console.log("check tx receipt timeout:" + hash);
+ checkTimes = 0;
+ callback();
+ return;
+ }
+
+ neb.api.getTransactionReceipt(hash).then(function (resp) {
+
+ console.log("tx receipt status:" + resp.status);
+ if (resp.status === 2) {
+ setTimeout(function () {
+ checkTransaction(hash, callback);
+ }, 2000);
+ } else {
+ checkTimes = 0;
+ var endCheckTime = new Date().getTime();
+ console.log("check tx time: : " + (endCheckTime - beginCheckTime) / 1000);
+ callback(resp);
+ }
+ }).catch(function (err) {
+ console.log("fail to get tx receipt hash: " + hash);
+ console.log("it may becuase the tx is being packing, we are going on to check it!");
+ // console.log(err);
+ setTimeout(function () {
+ checkTransaction(hash, callback);
+ }, 2000);
+ });
+}
+
+describe('test transfer from contract', function () {
+ before('0. deploy contract', function (done) {
+ try {
+ neb.api.getAccountState(sourceAccount.getAddressString()).then(function(resp) {
+ console.log("----step0. get source account state: " + JSON.stringify(resp));
+ var contractSource = FS.readFileSync("./nf/nvm/test/test_contract_features.js", "UTF-8");
+ var contract = {
+ 'source': contractSource,
+ "sourceType": "js",
+ "args": ''
+ };
+ nonce = parseInt(resp.nonce);
+ nonce = nonce + 1;
+ var tx = new Transaction(ChainID, sourceAccount, sourceAccount, 0, nonce, 1000000, 20000000, contract);
+ tx.signTransaction();
+ return neb.api.sendRawTransaction(tx.toProtoString());
+ }).then(function(resp) {
+ console.log("----step1. deploy contract: " + JSON.stringify(resp));
+ contractAddress = resp.contract_address;
+ checkTransaction(resp.txhash, function(resp) {
+ try {
+ expect(resp).to.not.be.a('undefined');
+ expect(resp.status).to.be.equal(1);
+ console.log("----step2. have been on chain");
+ done();
+ } catch(err) {
+ console.log(err);
+ done(err);
+ }
+ });
+ }).catch(function(err) {
+ console.log("unexpected err: " + err);
+ done(err);
+ });
+ } catch (err) {
+ console.log("unexpected err: " + err);
+ done(err);
+ }
+ });
+
+ it ('1. test feature getAccoutState 1/2', function (done) {
+ var contract = {
+ 'function': 'testGetAccountState1',
+ "args": '[]',
+ };
+ neb.api.call(sourceAccount.getAddressString(), contractAddress, Unit.nasToBasic(10), nonce, 200000, 1000000, contract).then(function(resp){
+ console.log(JSON.stringify(resp));
+ expect(resp.execute_err).to.be.equal("");
+ done();
+ }).catch(function(err) {
+ console.log(err);
+ done(err);
+ });
+ });
+
+ it ('2. test feature getAccoutState 2/2', function (done) {
+ var contract = {
+ 'function': 'testGetAccountState2',
+ "args": '[]',
+ };
+ neb.api.call(sourceAccount.getAddressString(), contractAddress, Unit.nasToBasic(10), nonce, 200000, 1000000, contract).then(function(resp){
+ console.log(JSON.stringify(resp));
+ expect(resp.execute_err).to.be.equal("Call: Blockchain.getAccountState(), parse address failed");
+ done();
+ }).catch(function(err) {
+ console.log(err);
+ done(err);
+ });
+ });
+
+ it ('3. test feature getPreBlockHash 1/5[offset is 1]', function (done) {
+ var offset = 1;
+ var contract = {
+ 'function': 'testGetPreBlockHash',
+ "args": '[' + offset + ']',
+ };
+ var hash;
+ var height;
+ neb.api.call(sourceAccount.getAddressString(), contractAddress, Unit.nasToBasic(10), nonce, 200000, 1000000, contract).then(function(resp){
+ console.log(JSON.stringify(resp));
+ var result = JSON.parse(resp.result);
+ hash = result.hash;
+ height = result.height;
+ return neb.api.getBlockByHeight(height - offset);
+ }).then(function(resp) {
+ // console.log(JSON.stringify(resp));
+ expect(resp.hash).to.be.equal(hash);
+ done();
+ }).catch(function(err) {
+ console.log(err);
+ done(err);
+ });
+ });
+
+ it ('4. test feature getPreBlockHash 2/5[offset is "1"]', function (done) {
+ var offset = 1;
+ var contract = {
+ 'function': 'testGetPreBlockHash',
+ "args": "[\"" + offset + "\"]",
+ };
+ var hash;
+ var height;
+ neb.api.call(sourceAccount.getAddressString(), contractAddress, Unit.nasToBasic(10), nonce, 200000, 1000000, contract).then(function(resp){
+ console.log(JSON.stringify(resp));
+ var result = JSON.parse(resp.result);
+ hash = result.hash;
+ height = result.height;
+ return neb.api.getBlockByHeight(height - offset);
+ }).then(function(resp) {
+ // console.log(JSON.stringify(resp));
+ expect(resp.hash).to.be.equal(hash);
+ done();
+ }).catch(function(err) {
+ console.log(err);
+ done(err);
+ });
+ });
+
+ it ('5. test feature getPreBlockHash 3/5[offset is "#1"]', function (done) {
+ var offset = "#1";
+ var contract = {
+ 'function': 'testGetPreBlockHash',
+ "args": "[\"" + offset + "\"]",
+ };
+ neb.api.call(sourceAccount.getAddressString(), contractAddress, Unit.nasToBasic(10), nonce, 200000, 1000000, contract).then(function(resp){
+ console.log(JSON.stringify(resp));
+ expect(resp.execute_err).to.be.equal("Call: getPreBlockHash: invalid offset");
+ done();
+ }).catch(function(err) {
+ console.log(err);
+ done(err);
+ });
+ });
+
+ it ('6. test feature getPreBlockHash 4/5[offset is 0]', function (done) {
+ var offset = 0;
+ var contract = {
+ 'function': 'testGetPreBlockHash',
+ "args": '[' + offset + ']',
+ };
+ neb.api.call(sourceAccount.getAddressString(), contractAddress, Unit.nasToBasic(10), nonce, 200000, 1000000, contract).then(function(resp){
+ console.log(JSON.stringify(resp));
+ expect(resp.execute_err).to.be.equal("Call: getPreBlockHash: invalid offset");
+ done();
+ }).catch(function(err) {
+ console.log(err);
+ done(err);
+ });
+ });
+
+ it ('7. test feature getPreBlockHash 5/5[offset is too large]', function (done) {
+ var offset = 11111111111111;
+ var contract = {
+ 'function': 'testGetPreBlockHash',
+ "args": '[' + offset + ']',
+ };
+ neb.api.call(sourceAccount.getAddressString(), contractAddress, Unit.nasToBasic(10), nonce, 200000, 1000000, contract).then(function(resp){
+ console.log(JSON.stringify(resp));
+ expect(resp.execute_err).to.be.equal("Call: getPreBlockHash: block not exist");
+ done();
+ }).catch(function(err) {
+ console.log(err);
+ done(err);
+ });
+ });
+
+ it ('8. test feature getPreBlockSeed 1/5[offset is 1]', function (done) {
+ var offset = 1;
+ var contract = {
+ 'function': 'testGetPreBlockSeed',
+ "args": '[' + offset + ']',
+ };
+ var seed;
+ var height;
+ neb.api.call(sourceAccount.getAddressString(), contractAddress, Unit.nasToBasic(10), nonce, 200000, 1000000, contract).then(function(resp){
+ console.log(JSON.stringify(resp));
+ var result = JSON.parse(resp.result);
+ seed = result.seed;
+ console.log(seed);
+ height = result.height;
+ return neb.api.getBlockByHeight(height - offset);
+ }).then(function(resp) {
+ console.log(JSON.stringify(resp));
+ expect(resp.randomSeed).to.be.equal(seed);
+ done();
+ }).catch(function(err) {
+ console.log(err);
+ done(err);
+ });
+ });
+
+ it ('9. test feature getPreBlockSeed 2/5[offset is "1"]', function (done) {
+ var offset = 1;
+ var contract = {
+ 'function': 'testGetPreBlockSeed',
+ "args": "[\"" + offset + "\"]",
+ };
+ var seed;
+ var height;
+ neb.api.call(sourceAccount.getAddressString(), contractAddress, Unit.nasToBasic(10), nonce, 200000, 1000000, contract).then(function(resp){
+ console.log(JSON.stringify(resp));
+ var result = JSON.parse(resp.result);
+ seed = result.seed;
+ height = result.height;
+ return neb.api.getBlockByHeight(height - offset);
+ }).then(function(resp) {
+ // console.log(JSON.stringify(resp));
+ expect(resp.randomSeed).to.be.equal(seed);
+ done();
+ }).catch(function(err) {
+ console.log(err);
+ done(err);
+ });
+ });
+
+ it ('10. test feature getPreBlockSeed 3/5[offset is 0]', function (done) {
+ var offset = 0;
+ var contract = {
+ 'function': 'testGetPreBlockSeed',
+ "args": '[' + offset + ']',
+ };
+ neb.api.call(sourceAccount.getAddressString(), contractAddress, Unit.nasToBasic(10), nonce, 200000, 1000000, contract).then(function(resp){
+ console.log(JSON.stringify(resp));
+ expect(resp.execute_err).to.be.equal("Call: getPreBlockSeed: invalid offset");
+ done();
+ }).catch(function(err) {
+ console.log(err);
+ done(err);
+ });
+ });
+
+ it ('11. test feature getPreBlockSeed 4/5[offset is too large]', function (done) {
+ var offset = 11111111111111;
+ var contract = {
+ 'function': 'testGetPreBlockSeed',
+ "args": '[' + offset + ']',
+ };
+ neb.api.call(sourceAccount.getAddressString(), contractAddress, Unit.nasToBasic(10), nonce, 200000, 1000000, contract).then(function(resp){
+ console.log(JSON.stringify(resp));
+ expect(resp.execute_err).to.be.equal("Call: getPreBlockSeed: block not exist");
+ done();
+ }).catch(function(err) {
+ console.log(err);
+ done(err);
+ });
+ });
+ it ('12. test feature getPreBlockSeed 5/5[offset is "#1"]', function (done) {
+ var offset = "#1";
+ var contract = {
+ 'function': 'testGetPreBlockSeed',
+ "args": "[\"" + offset + "\"]",
+ };
+ neb.api.call(sourceAccount.getAddressString(), contractAddress, Unit.nasToBasic(10), nonce, 200000, 1000000, contract).then(function(resp){
+ console.log(JSON.stringify(resp));
+ expect(resp.execute_err).to.be.equal("Call: getPreBlockSeed: invalid offset");
+ done();
+ }).catch(function(err) {
+ console.log(err);
+ done(err);
+ });
+ });
+});
diff --git a/nebtestkit/cases/contract/contract.lib.crypto.test.js b/nebtestkit/cases/contract/contract.lib.crypto.test.js
new file mode 100644
index 000000000..6c170b1c0
--- /dev/null
+++ b/nebtestkit/cases/contract/contract.lib.crypto.test.js
@@ -0,0 +1,477 @@
+'use strict';
+
+var sleep = require('system-sleep');
+var FS = require('fs');
+var expect = require('chai').expect;
+var BigNumber = require('bignumber.js');
+var HttpRequest = require('../../node-request');
+var TestnetConfig = require('../testnet_config');
+var Wallet = require('nebulas');
+
+var Account = Wallet.Account;
+var Transaction = Wallet.Transaction;
+var env = process.argv.splice(2)[1];
+var testnetConfig = new TestnetConfig(env);
+var originSource = testnetConfig.sourceAccount;
+var ChainID = testnetConfig.ChainId;
+var coinbase = testnetConfig.coinbase;
+var neb = new Wallet.Neb();
+neb.setRequest(new HttpRequest(testnetConfig.apiEndPoint));
+
+
+var deploy, from, contractAddr, source, fromState, coinState;
+var caseIndex = 0, lastnonce = 0;
+
+function prepareSource(done) {
+ console.log("originSource address: " + originSource.getAddressString());
+ neb.api.getAccountState(originSource.getAddressString()).then(function (resp) {
+ console.log("prepare source account state:" + JSON.stringify(resp));
+ var nonce = parseInt(resp.nonce);
+
+ source = Account.NewAccount();
+
+ var tx = new Transaction(ChainID, originSource, source, neb.nasToBasic(1000), nonce + 1, "1000000", "200000");
+ tx.signTransaction();
+
+ console.log("cliam source tx:", tx.toString());
+
+ return neb.api.sendRawTransaction(tx.toProtoString());
+ }).then(function (resp) {
+ console.log("send Raw Tx:" + JSON.stringify(resp));
+ expect(resp).to.be.have.property('txhash');
+ checkTransaction(resp.txhash, 0, function (receipt) {
+ console.log("tx receipt : " + JSON.stringify(receipt));
+ expect(receipt).to.be.have.property('status').equal(1);
+
+ done();
+ });
+ }).catch(function (err) {
+ done(err);
+ });
+}
+
+function cliamTokens(accounts, values, done) {
+ for (var i = 0; i < accounts.length; i++) {
+ console.log("acc:"+accounts[i].getAddressString()+" value:"+values[i]);
+ sendTransaction(source, accounts[i], values[i], ++lastnonce);
+ sleep(30);
+ }
+ checkCliamTokens(done);
+}
+
+
+function sendTransaction(from, address, value, nonce) {
+ var transaction = new Transaction(ChainID, from, address, value, nonce, "1000000", "200000");
+ transaction.signTransaction();
+ var rawTx = transaction.toProtoString();
+ neb.api.sendRawTransaction(rawTx).then(function (resp) {
+ console.log("send raw transaction resp:" + JSON.stringify(resp));
+ });
+}
+
+function checkCliamTokens(done) {
+ var intervalAccount = setInterval(function () {
+ neb.api.getAccountState(source.getAddressString()).then(function (resp) {
+ // console.log("master accountState resp:" + JSON.stringify(resp));
+ var nonce = parseInt(resp.nonce);
+ console.log("check cliam tokens nonce:", lastnonce);
+
+ if (lastnonce <= nonce){
+ console.log("cliam tokens success");
+ clearInterval(intervalAccount);
+ done();
+ }
+ });
+ }, 2000);
+}
+
+function checkTransaction(txhash, retry, done){
+
+ var maxRetry = 45;
+
+ // contract status and get contract_address
+ var interval = setTimeout(function () {
+ neb.api.getTransactionReceipt(txhash).then(function (resp) {
+ retry++;
+
+ console.log("check transaction status:" + resp.status);
+ if(resp.status && resp.status === 1) {
+ // clearInterval(interval);
+
+ if (resp.contract_address) {
+ console.log("deploy private key:" + deploy.getPrivateKeyString());
+ console.log("deploy address:" + deploy.getAddressString());
+ console.log("deploy contract address:" + resp.contract_address);
+
+ contractAddr = resp.contract_address;
+ }
+
+ done(resp);
+ } else if (resp.status && resp.status === 2) {
+ if (retry > maxRetry) {
+ console.log("check transaction time out");
+ // clearInterval(interval);
+ done(resp);
+ } else {
+ checkTransaction(txhash, retry++, done);
+ }
+ } else {
+ // clearInterval(interval);
+ console.log("transaction execution failed");
+ done(resp);
+ }
+ }).catch(function (err) {
+ retry++;
+ console.log("check transaction not found retry " + retry);
+ if (retry > maxRetry) {
+ console.log(JSON.stringify(err.error));
+ // clearInterval(interval);
+ done(err);
+ } else {
+ checkTransaction(txhash, retry++, done);
+ }
+ });
+
+ }, 2000);
+}
+
+
+function deployContract(done, caseGroup) {
+ console.log("start deploying contract: " + caseGroup.groupname);
+
+ neb.api.getAccountState(source.getAddressString()).then(function (resp) {
+ console.log("source account state:" + JSON.stringify(resp));
+
+ var accounts = new Array();
+ var values = new Array();
+ deploy = Account.NewAccount();
+ accounts.push(deploy);
+ values.push(neb.nasToBasic(1));
+
+ from = Account.NewAccount();
+ accounts.push(from);
+ var fromBalance = (typeof caseGroup.fromBalance === "undefined") ? neb.nasToBasic(1) : caseGroup.fromBalance;
+ values.push(fromBalance);
+
+ cliamTokens(accounts, values, () => {
+ try {
+ var source = FS.readFileSync("../../../nf/nvm/test/" + caseGroup.filename, "utf-8");
+ var contract = {
+ "source": source,
+ "sourceType": caseGroup.type,
+ "args": ""
+ };
+
+ var tx = new Transaction(testnetConfig.ChainId, deploy, deploy, "0", 1, "10000000", "2000000", contract);
+ tx.signTransaction();
+ var rawTx = tx.toProtoString();
+
+ // console.log("contract:" + rawTx);
+ neb.api.sendRawTransaction(rawTx).then(function (resp) {
+ console.log("deploy contract " + caseGroup.groupname + " return: " + JSON.stringify(resp));
+
+ checkTransaction(resp.txhash, 0, (ret) => {
+ if (ret.status && ret.status === 1) {
+ done();
+ } else {
+ done(ret);
+ }
+ });
+ });
+ } catch (err) {
+ done(err);
+ };
+ });
+
+ }).catch (err => done(err));
+}
+
+function runTest(testInput, testExpect, done) {
+ var fromAcc = (typeof testInput.from === "undefined") ? from : testInput.from;
+ var to = (typeof testInput.to === "undefined") ? Account.fromAddress(contractAddr) : testInput.to;
+
+ var fromBalanceBefore, toBalanceBefore;
+
+ neb.api.getAccountState(to.getAddressString()).then(function (resp) {
+ console.log("contractAddr state before: " + JSON.stringify(resp));
+ toBalanceBefore = resp.balance;
+ return neb.api.getAccountState(from.getAddressString());
+ }).then(resp => {
+ fromState = resp;
+ fromBalanceBefore = resp.balanece;
+ console.log("from state before: ", JSON.stringify(resp));
+ return neb.api.getAccountState(coinbase);
+ }).then(function (resp) {
+ console.log("coin state before: ", JSON.stringify(resp));
+ coinState = resp;
+
+ var tx = new Transaction(ChainID, fromAcc, to, testInput.value, parseInt(fromState.nonce) + testInput.nonce, testInput.gasPrice, testInput.gasLimit, testInput.contract);
+ tx.from.address = fromAcc.address;
+ tx.to.address = to.address;
+ tx.gasPrice = new BigNumber(testInput.gasPrice);
+ tx.gasLimit = new BigNumber(testInput.gasLimit);
+ tx.signTransaction();
+ console.log("binary tx raw before send: ", tx.toString());
+ return neb.api.sendRawTransaction(tx.toProtoString());
+ }).then(function (rawResp) {
+ console.log("send Raw Tx return:" + JSON.stringify(rawResp));
+ expect(rawResp).to.be.have.property('txhash');
+
+ checkTransaction(rawResp.txhash, 0, function (receipt) {
+ console.log("tx receipt : " + JSON.stringify(receipt));
+ try {
+ expect(receipt).to.not.be.a('undefined');
+ if (true === testExpect.canExcuteTx) {
+ expect(receipt).to.be.have.property('status').equal(1);
+ } else {
+ expect(receipt).to.be.have.property('status').equal(0);
+ }
+
+ neb.api.getAccountState(receipt.from).then(function (state) {
+
+ console.log("from state after: " + JSON.stringify(state));
+ // expect(state.balance).to.equal(testExpect.fromBalanceAfterTx);
+ return neb.api.getAccountState(contractAddr);
+ }).then(function (state) {
+
+ console.log("contractAddr state after: " + JSON.stringify(state));
+ var change = new BigNumber(state.balance).minus(new BigNumber(toBalanceBefore));
+ // expect(change.toString()).to.equal(testExpect.toBalanceChange);
+ return neb.api.getAccountState(coinbase);
+ }).then(function (state) {
+
+ console.log("get coinbase account state before tx:" + JSON.stringify(coinState));
+ console.log("get coinbase account state after tx:" + JSON.stringify(state));
+ var reward = new BigNumber(state.balance).sub(coinState.balance);
+ reward = reward.mod(new BigNumber(1.42694).mul(new BigNumber(10).pow(18)));
+ // The transaction should be only
+ // expect(reward.toString()).to.equal(testExpect.transferReward);
+ console.log("coinbase reward: " + reward.toString());
+ if (receipt.gasUsed) {
+ var txCost = new BigNumber(receipt.gasUsed).mul(receipt.gasPrice).toString(10);
+ // expect(txCost).to.equal(testExpect.transferReward);
+ console.log("tx cost gas: " + txCost.toString());
+ }
+
+ return neb.api.getEventsByHash(receipt.hash);
+ }).then(function (events) {
+ for (var i = 0; i < events.events.length; i++) {
+ var event = events.events[i];
+ //console.log("tx event:", JSON.stringify(event,null,'\t'));
+ console.log("tx event data:", event.data);
+ if (event.topic === "chain.transactionResult") {
+ var result = JSON.parse(event.data);
+ expect(result.status).to.equal(testExpect.status);
+
+ if (testExpect.hasOwnProperty("eventErr")){
+ console.log("Event error checked.");
+ expect(result.error).to.equal(testExpect.eventErr);
+ }
+
+ if (testExpect.hasOwnProperty("result")){
+ console.log("Result checked.");
+ expect(result.execute_result).to.equal(testExpect.result);
+ }
+ }
+ }
+ done();
+ }).catch(function (err) {
+ console.log("exe tx err:", err);
+ done(err);
+ });
+ } catch (err) {
+ console.log("submit tx err:", err.message);
+ done(err);
+ }
+ });
+ }).catch(function (err) {
+ if (err.error && err.error.error && testExpect.eventErr) {
+ try {
+ expect(err.error.error).to.equal(testExpect.eventErr)
+ done();
+ } catch (err) {
+ done(err);
+ }
+ return;
+ }
+ done(err);
+ });
+}
+
+var testCaseGroups = [];
+var caseGroup = {
+ "filename": "contract_crypto.js",
+ "type": "js",
+ "groupname": "case group 0",
+ "groupIndex": 0,
+
+ cases: [
+ {
+ "name": "0-1. test sha256",
+ "testInput": {
+ value: "0",
+ nonce: 1,
+ gasPrice: 1000000,
+ gasLimit: 2000000,
+ contract: {
+ function: "testSha256",
+ args: "[\"Nebulas is a next generation public blockchain, aiming for a continuously improving ecosystem.\"]"
+ }
+ },
+ "testExpect": {
+ canExcuteTx: true,
+ toBalanceChange: "0",
+ status: 1,
+ // eventErr: "Call: Error: input seed must be a string",
+ result: "\"a32d6d686968192663b9c9e21e6a3ba1ba9b2e288470c2f98b790256530933e0\""
+ }
+ },
+ {
+ "name": "0-2. test sha3256",
+ "testInput": {
+ value: "0",
+ nonce: 1,
+ gasPrice: 1000000,
+ gasLimit: 2000000,
+ contract: {
+ function: "testSha3256",
+ args: "[\"Nebulas is a next generation public blockchain, aiming for a continuously improving ecosystem.\"]"
+ }
+ },
+ "testExpect": {
+ canExcuteTx: true,
+ toBalanceChange: "0",
+ status: 1,
+ result: "\"564733f9f3e139b925cfb1e7e50ba8581e9107b13e4213f2e4708d9c284be75b\""
+ }
+ },
+ {
+ "name": "0-3. test ripemd160",
+ "testInput": {
+ value: "0",
+ nonce: 1,
+ gasPrice: 1000000,
+ gasLimit: 2000000,
+ contract: {
+ function: "testRipemd160",
+ args: "[\"Nebulas is a next generation public blockchain, aiming for a continuously improving ecosystem.\"]"
+ }
+ },
+ "testExpect": {
+ canExcuteTx: true,
+ toBalanceChange: "0",
+ status: 1,
+ result: "\"4236aa9974eb7b9ddb0f7a7ed06d4bf3d9c0e386\""
+ }
+ },
+ {
+ "name": "0-4. test recoverAddress",
+ "testInput": {
+ value: "0",
+ nonce: 1,
+ gasPrice: 1000000,
+ gasLimit: 2000000,
+ contract: {
+ function: "testRecoverAddress",
+ args: "[1,\"564733f9f3e139b925cfb1e7e50ba8581e9107b13e4213f2e4708d9c284be75b\",\"d80e282d165f8c05d8581133df7af3c7c41d51ec7cd8470c18b84a31b9af6a9d1da876ab28a88b0226707744679d4e180691aca6bdef5827622396751a0670c101\"]"
+ }
+ },
+ "testExpect": {
+ canExcuteTx: true,
+ toBalanceChange: "0",
+ status: 1,
+ result: "\"n1F8QbdnhqpPXDPFT2c9a581tpia8iuF7o2\""
+ }
+ },
+ {
+ "name": "0-5. test recoverAddress invalid alg",
+ "testInput": {
+ value: "0",
+ nonce: 1,
+ gasPrice: 1000000,
+ gasLimit: 2000000,
+ contract: {
+ function: "testRecoverAddress",
+ args: "[10,\"564733f9f3e139b925cfb1e7e50ba8581e9107b13e4213f2e4708d9c284be75b\",\"d80e282d165f8c05d8581133df7af3c7c41d51ec7cd8470c18b84a31b9af6a9d1da876ab28a88b0226707744679d4e180691aca6bdef5827622396751a0670c101\"]"
+ }
+ },
+ "testExpect": {
+ canExcuteTx: true,
+ toBalanceChange: "0",
+ status: 1,
+ result: "null"
+ }
+ },
+ {
+ "name": "0-6. test md5",
+ "testInput": {
+ value: "0",
+ nonce: 1,
+ gasPrice: 1000000,
+ gasLimit: 2000000,
+ contract: {
+ function: "testMd5",
+ args: "[\"Nebulas is a next generation public blockchain, aiming for a continuously improving ecosystem.\"]"
+ }
+ },
+ "testExpect": {
+ canExcuteTx: true,
+ toBalanceChange: "0",
+ status: 1,
+ result: "\"9954125a33a380c3117269cff93f76a7\""
+ }
+ },
+ {
+ "name": "0-7. test base64",
+ "testInput": {
+ value: "0",
+ nonce: 1,
+ gasPrice: 1000000,
+ gasLimit: 2000000,
+ contract: {
+ function: "testBase64",
+ args: "[\"https://y.qq.com/portal/player_radio.html#id=99\"]"
+ }
+ },
+ "testExpect": {
+ canExcuteTx: true,
+ toBalanceChange: "0",
+ status: 1,
+ result: "\"aHR0cHM6Ly95LnFxLmNvbS9wb3J0YWwvcGxheWVyX3JhZGlvLmh0bWwjaWQ9OTk=\""
+ }
+ }
+ ]
+};
+testCaseGroups.push(caseGroup);
+
+describe('Contract crypto test', () => {
+
+ before(done => prepareSource(done));
+
+ for (var i = 0; i < testCaseGroups.length; i++) {
+
+ // if (i != 3) {continue;} // selectively run tests
+
+ let caseGroup = testCaseGroups[i];
+ describe(caseGroup.groupname, () => {
+ before(done => {
+ deployContract(done, caseGroup);
+ caseIndex = 0;
+ });
+
+
+ for (var j = 0; j < caseGroup.cases.length; j++) {
+ let testCase = caseGroup.cases[j];
+ it(testCase.name, done => {
+ console.log("===> running case: " + JSON.stringify(testCase));
+ runTest(testCase.testInput, testCase.testExpect, done);
+ });
+ }
+
+ afterEach(() => {
+ caseIndex++;
+ console.log("case group: " + caseGroup.groupIndex + ", index: " + caseIndex);
+ });
+ });
+ }
+});
\ No newline at end of file
diff --git a/nebtestkit/cases/contract/contract_date_and_uint.test.js b/nebtestkit/cases/contract/contract_date_and_uint.test.js
new file mode 100644
index 000000000..82d209e64
--- /dev/null
+++ b/nebtestkit/cases/contract/contract_date_and_uint.test.js
@@ -0,0 +1,387 @@
+'use strict';
+
+var sleep = require('system-sleep');
+var FS = require('fs');
+var expect = require('chai').expect;
+var BigNumber = require('bignumber.js');
+var HttpRequest = require('../../node-request');
+var TestnetConfig = require('../testnet_config');
+var Wallet = require('nebulas');
+
+var Account = Wallet.Account;
+var Transaction = Wallet.Transaction;
+var env = process.argv.splice(2)[1];
+var testnetConfig = new TestnetConfig(env);
+var originSource = testnetConfig.sourceAccount;
+var ChainID = testnetConfig.ChainId;
+var coinbase = testnetConfig.coinbase;
+var neb = new Wallet.Neb();
+neb.setRequest(new HttpRequest(testnetConfig.apiEndPoint));
+
+
+var deploy, from, contractAddr, source, fromState, coinState;
+var caseIndex = 0, lastnonce = 0;
+
+function prepareSource(done) {
+ console.log("originSource address: " + originSource.getAddressString());
+ neb.api.getAccountState(originSource.getAddressString()).then(function (resp) {
+ console.log("prepare source account state:" + JSON.stringify(resp));
+ var nonce = parseInt(resp.nonce);
+
+ source = Account.NewAccount();
+
+ var tx = new Transaction(ChainID, originSource, source, neb.nasToBasic(1000), nonce + 1, "1000000", "200000");
+ tx.signTransaction();
+
+ console.log("cliam source tx:", tx.toString());
+
+ return neb.api.sendRawTransaction(tx.toProtoString());
+ }).then(function (resp) {
+ console.log("send Raw Tx:" + JSON.stringify(resp));
+ expect(resp).to.be.have.property('txhash');
+ checkTransaction(resp.txhash, 0, function (receipt) {
+ console.log("tx receipt : " + JSON.stringify(receipt));
+ expect(receipt).to.be.have.property('status').equal(1);
+
+ done();
+ });
+ }).catch(function (err) {
+ done(err);
+ });
+}
+
+function cliamTokens(accounts, values, done) {
+ for (var i = 0; i < accounts.length; i++) {
+ console.log("acc:"+accounts[i].getAddressString()+" value:"+values[i]);
+ sendTransaction(source, accounts[i], values[i], ++lastnonce);
+ sleep(30);
+ }
+ checkCliamTokens(done);
+}
+
+
+function sendTransaction(from, address, value, nonce) {
+ var transaction = new Transaction(ChainID, from, address, value, nonce, "1000000", "200000");
+ transaction.signTransaction();
+ var rawTx = transaction.toProtoString();
+ neb.api.sendRawTransaction(rawTx).then(function (resp) {
+ console.log("send raw transaction resp:" + JSON.stringify(resp));
+ });
+}
+
+function checkCliamTokens(done) {
+ var intervalAccount = setInterval(function () {
+ neb.api.getAccountState(source.getAddressString()).then(function (resp) {
+ // console.log("master accountState resp:" + JSON.stringify(resp));
+ var nonce = parseInt(resp.nonce);
+ console.log("check cliam tokens nonce:", lastnonce);
+
+ if (lastnonce <= nonce){
+ console.log("cliam tokens success");
+ clearInterval(intervalAccount);
+ done();
+ }
+ });
+ }, 2000);
+}
+
+function checkTransaction(txhash, retry, done){
+
+ var maxRetry = 45;
+
+ // contract status and get contract_address
+ var interval = setTimeout(function () {
+ neb.api.getTransactionReceipt(txhash).then(function (resp) {
+ retry++;
+
+ console.log("check transaction status:" + resp.status);
+ if(resp.status && resp.status === 1) {
+ // clearInterval(interval);
+
+ if (resp.contract_address) {
+ console.log("deploy private key:" + deploy.getPrivateKeyString());
+ console.log("deploy address:" + deploy.getAddressString());
+ console.log("deploy contract address:" + resp.contract_address);
+
+ contractAddr = resp.contract_address;
+ }
+
+ done(resp);
+ } else if (resp.status && resp.status === 2) {
+ if (retry > maxRetry) {
+ console.log("check transaction time out");
+ // clearInterval(interval);
+ done(resp);
+ } else {
+ checkTransaction(txhash, retry++, done);
+ }
+ } else {
+ // clearInterval(interval);
+ console.log("transaction execution failed");
+ done(resp);
+ }
+ }).catch(function (err) {
+ retry++;
+ console.log("check transaction not found retry " + retry);
+ if (retry > maxRetry) {
+ console.log(JSON.stringify(err.error));
+ // clearInterval(interval);
+ done(err);
+ } else {
+ checkTransaction(txhash, retry++, done);
+ }
+ });
+
+ }, 2000);
+}
+
+
+function deployContract(done, caseGroup) {
+ console.log("start deploying contract: " + caseGroup.groupname);
+
+ neb.api.getAccountState(source.getAddressString()).then(function (resp) {
+ console.log("source account state:" + JSON.stringify(resp));
+
+ var accounts = new Array();
+ var values = new Array();
+ deploy = Account.NewAccount();
+ accounts.push(deploy);
+ values.push(neb.nasToBasic(1));
+
+ from = Account.NewAccount();
+ accounts.push(from);
+ var fromBalance = (typeof caseGroup.fromBalance === "undefined") ? neb.nasToBasic(1) : caseGroup.fromBalance;
+ values.push(fromBalance);
+
+ cliamTokens(accounts, values, () => {
+ try {
+ var source = FS.readFileSync("../../../nf/nvm/test/" + caseGroup.filename, "utf-8");
+ var contract = {
+ "source": source,
+ "sourceType": caseGroup.type,
+ "args": ""
+ };
+
+ var tx = new Transaction(testnetConfig.ChainId, deploy, deploy, "0", 1, "10000000", "2000000", contract);
+ tx.signTransaction();
+ var rawTx = tx.toProtoString();
+
+ // console.log("contract:" + rawTx);
+ neb.api.sendRawTransaction(rawTx).then(function (resp) {
+ console.log("deploy contract " + caseGroup.groupname + " return: " + JSON.stringify(resp));
+
+ checkTransaction(resp.txhash, 0, (ret) => {
+ if (ret.status && ret.status === 1) {
+ done();
+ } else {
+ done(ret);
+ }
+ });
+ });
+ } catch (err) {
+ done(err);
+ };
+ });
+
+ }).catch (err => done(err));
+}
+
+function runTest(testInput, testExpect, done) {
+ var fromAcc = (typeof testInput.from === "undefined") ? from : testInput.from;
+ var to = (typeof testInput.to === "undefined") ? Account.fromAddress(contractAddr) : testInput.to;
+
+ var fromBalanceBefore, toBalanceBefore;
+
+ neb.api.getAccountState(to.getAddressString()).then(function (resp) {
+ console.log("contractAddr state before: " + JSON.stringify(resp));
+ toBalanceBefore = resp.balance;
+ return neb.api.getAccountState(from.getAddressString());
+ }).then(resp => {
+ fromState = resp;
+ fromBalanceBefore = resp.balanece;
+ console.log("from state before: ", JSON.stringify(resp));
+ return neb.api.getAccountState(coinbase);
+ }).then(function (resp) {
+ console.log("coin state before: ", JSON.stringify(resp));
+ coinState = resp;
+
+ var tx = new Transaction(ChainID, fromAcc, to, testInput.value, parseInt(fromState.nonce) + testInput.nonce, testInput.gasPrice, testInput.gasLimit, testInput.contract);
+ tx.from.address = fromAcc.address;
+ tx.to.address = to.address;
+ tx.gasPrice = new BigNumber(testInput.gasPrice);
+ tx.gasLimit = new BigNumber(testInput.gasLimit);
+ tx.signTransaction();
+ console.log("binary tx raw before send: ", tx.toString());
+ return neb.api.sendRawTransaction(tx.toProtoString());
+ }).then(function (rawResp) {
+ console.log("send Raw Tx return:" + JSON.stringify(rawResp));
+ expect(rawResp).to.be.have.property('txhash');
+
+ checkTransaction(rawResp.txhash, 0, function (receipt) {
+ console.log("tx receipt : " + JSON.stringify(receipt));
+ try {
+ expect(receipt).to.not.be.a('undefined');
+ if (true === testExpect.canExcuteTx) {
+ expect(receipt).to.be.have.property('status').equal(1);
+ } else {
+ expect(receipt).to.be.have.property('status').equal(0);
+ }
+
+ neb.api.getAccountState(receipt.from).then(function (state) {
+
+ console.log("from state after: " + JSON.stringify(state));
+ // expect(state.balance).to.equal(testExpect.fromBalanceAfterTx);
+ return neb.api.getAccountState(contractAddr);
+ }).then(function (state) {
+
+ console.log("contractAddr state after: " + JSON.stringify(state));
+ var change = new BigNumber(state.balance).minus(new BigNumber(toBalanceBefore));
+ // expect(change.toString()).to.equal(testExpect.toBalanceChange);
+ return neb.api.getAccountState(coinbase);
+ }).then(function (state) {
+
+ console.log("get coinbase account state before tx:" + JSON.stringify(coinState));
+ console.log("get coinbase account state after tx:" + JSON.stringify(state));
+ var reward = new BigNumber(state.balance).sub(coinState.balance);
+ reward = reward.mod(new BigNumber(1.42694).mul(new BigNumber(10).pow(18)));
+ // The transaction should be only
+ // expect(reward.toString()).to.equal(testExpect.transferReward);
+ console.log("coinbase reward: " + reward.toString());
+ if (receipt.gasUsed) {
+ var txCost = new BigNumber(receipt.gasUsed).mul(receipt.gasPrice).toString(10);
+ // expect(txCost).to.equal(testExpect.transferReward);
+ console.log("tx cost gas: " + txCost.toString());
+ }
+
+ return neb.api.getEventsByHash(receipt.hash);
+ }).then(function (events) {
+ for (var i = 0; i < events.events.length; i++) {
+ var event = events.events[i];
+ //console.log("tx event:", JSON.stringify(event,null,'\t'));
+ console.log("tx event:", event.data);
+ if (event.topic === "chain.transactionResult") {
+ var result = JSON.parse(event.data);
+ expect(result.status).to.equal(testExpect.status);
+
+ if (testExpect.hasOwnProperty("eventErr")){
+ console.log("Event error checked.");
+ expect(result.error).to.equal(testExpect.eventErr);
+ }
+ }
+ if (event.topic === "chain.contract.Date") {
+ var result = JSON.parse(event.data);
+ expect(result.data.equalBlockTime).to.equal(testExpect.equalBlockTime);
+ console.log("check equalBlockTime success");
+ }
+
+ if (event.topic === "chain.contract.Uint64") {
+ var result = JSON.parse(event.data);
+ expect(result.data.incompatible).to.equal(testExpect.incompatible);
+ console.log("check incompatible success");
+ }
+ }
+ done();
+ }).catch(function (err) {
+ console.log("exe tx err:", err);
+ done(err);
+ });
+ } catch (err) {
+ console.log("submit tx err:", err.message);
+ done(err);
+ }
+ });
+ }).catch(function (err) {
+ if (err.error && err.error.error && testExpect.eventErr) {
+ try {
+ expect(err.error.error).to.equal(testExpect.eventErr)
+ done();
+ } catch (err) {
+ done(err);
+ }
+ return;
+ }
+ done(err);
+ });
+}
+
+var testCaseGroups = [];
+var caseGroup = {
+ "filename": "contract_date_and_uint.js",
+ "type": "js",
+ "groupname": "case group 0",
+ "groupIndex": 0,
+
+ cases: [
+ {
+ "name": "0-1. test date",
+ "testInput": {
+ value: "0",
+ nonce: 1,
+ gasPrice: 1000000,
+ gasLimit: 2000000,
+ contract: {
+ function: "testDate",
+ args: ""
+ }
+ },
+ "testExpect": {
+ canExcuteTx: true,
+ toBalanceChange: "0",
+ status: 1,
+ equalBlockTime: true
+ }
+ },
+ {
+ "name": "0-2. test uint",
+ "testInput": {
+ value: "0",
+ nonce: 1,
+ gasPrice: 1000000,
+ gasLimit: 2000000,
+ contract: {
+ function: "testUint64",
+ args: ""
+ }
+ },
+ "testExpect": {
+ canExcuteTx: true,
+ toBalanceChange: "0",
+ status: 1,
+ incompatible: true
+ }
+ }
+ ]
+};
+testCaseGroups.push(caseGroup);
+
+describe('Date and uint test', () => {
+
+ before(done => prepareSource(done));
+
+ for (var i = 0; i < testCaseGroups.length; i++) {
+
+ // if (i != 3) {continue;} // selectively run tests
+
+ let caseGroup = testCaseGroups[i];
+ describe(caseGroup.groupname, () => {
+ before(done => {
+ deployContract(done, caseGroup);
+ caseIndex = 0;
+ });
+
+
+ for (var j = 0; j < caseGroup.cases.length; j++) {
+ let testCase = caseGroup.cases[j];
+ it(testCase.name, done => {
+ console.log("===> running case: " + JSON.stringify(testCase));
+ runTest(testCase.testInput, testCase.testExpect, done);
+ });
+ }
+
+ afterEach(() => {
+ caseIndex++;
+ console.log("case group: " + caseGroup.groupIndex + ", index: " + caseIndex);
+ });
+ });
+ }
+});
\ No newline at end of file
diff --git a/nebtestkit/cases/contract/transferFromContract.test.js b/nebtestkit/cases/contract/transferFromContract.test.js
index 181f9ddba..cd50f22bb 100644
--- a/nebtestkit/cases/contract/transferFromContract.test.js
+++ b/nebtestkit/cases/contract/transferFromContract.test.js
@@ -141,6 +141,7 @@ describe('test transfer from contract', function () {
console.log(tx.toString);
neb.api.sendRawTransaction(tx.toProtoString()).then(function (resp) {
console.log("----step5. call transferSpecialValue function", JSON.stringify(resp));
+ var hash = resp.txhash;
checkTransaction(resp.txhash, function(resp) {
try {
expect(resp).to.not.be.a('undefined');
@@ -150,6 +151,10 @@ describe('test transfer from contract', function () {
return neb.api.getAccountState(contractAddress);
}).then(function(resp){
expect(resp.balance).equal("5000000000000000000");
+ return neb.api.getEventsByHash(hash);
+ }).then(function(resp){
+ console.log(JSON.stringify(resp));
+ expect(resp.events[0].topic).equal("chain.transferFromContract");
done();
}).catch(function(err){
console.log("unexpected err :", err);
@@ -177,6 +182,7 @@ describe('test transfer from contract', function () {
tx.signTransaction();
neb.api.sendRawTransaction(tx.toProtoString()).then(function (resp) {
console.log("----step7. call transferSpecialValue function", JSON.stringify(resp));
+ var hash = resp.txhash;
checkTransaction(resp.txhash, function(resp) {
try {
expect(resp).to.not.be.a('undefined');
@@ -187,6 +193,11 @@ describe('test transfer from contract', function () {
return neb.api.getAccountState(contractAddress);
}).then(function(resp){
expect(resp.balance).equal("5000000000000000000");
+ return neb.api.getEventsByHash(hash);
+ }).then(function(resp){
+ console.log("======", JSON.stringify(resp))
+ expect(resp.events[0].topic).equal("chain.transferFromContract");
+ expect(JSON.parse(resp.events[0].data).error).equal("failed to sub balace from contract address");
done();
}).catch(function(err){
console.log("unexpected err :", err);
diff --git a/nebtestkit/cases/testnet_config.js b/nebtestkit/cases/testnet_config.js
index f53559abc..1ed49f21f 100644
--- a/nebtestkit/cases/testnet_config.js
+++ b/nebtestkit/cases/testnet_config.js
@@ -20,7 +20,7 @@ var TestNet = function (env) {
} else if (env === "testneb2") {
this.ChainId = 1002;
- this.sourceAccount = new Wallet.Account("25a3a441a34658e7a595a0eda222fa43ac51bd223017d17b420674fb6d0a4d52");
+ this.sourceAccount = new Wallet.Account("1d3fe06a53919e728315e2ccca41d4aa5b190845a79007797517e62dbc0df454");
this.coinbase = "n1SAeQRVn33bamxN4ehWUT7JGdxipwn8b17";
this.apiEndPoint = "http://34.205.26.12:8685";
@@ -48,7 +48,7 @@ var TestNet = function (env) {
} else if (env === "local") {
this.ChainId = 100;
- this.sourceAccount = new Wallet.Account("d80f115bdbba5ef215707a8d7053c16f4e65588fd50b0f83369ad142b99891b5");
+ this.sourceAccount = new Wallet.Account("97c264928fff01305bf7a6390056d86df30a3fbbbd6673f53e97fb1205769156");
this.coinbase = "n1QZMXSZtW7BUerroSms4axNfyBGyFGkrh5";
this.apiEndPoint = "http://127.0.0.1:8685";
diff --git a/nf/nvm/blockchain.go b/nf/nvm/blockchain.go
index f9a3089fa..9277fed49 100644
--- a/nf/nvm/blockchain.go
+++ b/nf/nvm/blockchain.go
@@ -18,6 +18,9 @@
package nvm
+/*
+#include "v8/lib/nvm_error.h"
+*/
import "C"
import (
@@ -25,7 +28,9 @@ import (
"encoding/json"
+ "github.com/gogo/protobuf/proto"
"github.com/nebulasio/go-nebulas/core"
+ "github.com/nebulasio/go-nebulas/core/pb"
"github.com/nebulasio/go-nebulas/core/state"
"github.com/nebulasio/go-nebulas/util"
"github.com/nebulasio/go-nebulas/util/byteutils"
@@ -42,7 +47,7 @@ func GetTxByHashFunc(handler unsafe.Pointer, hash *C.char, gasCnt *C.size_t) *C.
}
// calculate Gas.
- *gasCnt = C.size_t(1000)
+ *gasCnt = C.size_t(GetTxByHashGasBase)
txHash, err := byteutils.FromHex(C.GoString(hash))
if err != nil {
@@ -81,22 +86,23 @@ func GetTxByHashFunc(handler unsafe.Pointer, hash *C.char, gasCnt *C.size_t) *C.
// GetAccountStateFunc returns account info by address
//export GetAccountStateFunc
-func GetAccountStateFunc(handler unsafe.Pointer, address *C.char, gasCnt *C.size_t) *C.char {
+func GetAccountStateFunc(handler unsafe.Pointer, address *C.char, gasCnt *C.size_t,
+ result **C.char, exceptionInfo **C.char) int {
+ *result = nil
+ *exceptionInfo = nil
engine, _ := getEngineByStorageHandler(uint64(uintptr(handler)))
if engine == nil || engine.ctx.block == nil {
- return nil
+ logging.VLog().Error("Unexpected error: failed to get engine")
+ return C.NVM_UNEXPECTED_ERR
}
// calculate Gas.
- *gasCnt = C.size_t(1000)
+ *gasCnt = C.size_t(GetAccountStateGasBase)
addr, err := core.AddressParse(C.GoString(address))
if err != nil {
- logging.VLog().WithFields(logrus.Fields{
- "handler": uint64(uintptr(handler)),
- "key": C.GoString(address),
- }).Debug("GetAccountStateFunc parse address failed.")
- return nil
+ *exceptionInfo = C.CString("Blockchain.getAccountState(), parse address failed")
+ return C.NVM_EXCEPTION_ERR
}
acc, err := engine.ctx.state.GetOrCreateUserAccount(addr.Bytes())
@@ -105,35 +111,125 @@ func GetAccountStateFunc(handler unsafe.Pointer, address *C.char, gasCnt *C.size
"handler": uint64(uintptr(handler)),
"address": addr,
"err": err,
- }).Debug("GetAccountStateFunc get account state failed.")
- return nil
+ }).Error("Unexpected error: GetAccountStateFunc get account state failed")
+ return C.NVM_UNEXPECTED_ERR
}
state := toSerializableAccount(acc)
json, err := json.Marshal(state)
if err != nil {
- return nil
+ logging.VLog().WithFields(logrus.Fields{
+ "state": state,
+ "json": json,
+ "err": err,
+ }).Error("Unexpected error: GetAccountStateFunc failed to mashal account state")
+ return C.NVM_UNEXPECTED_ERR
+ }
+
+ *result = C.CString(string(json))
+ return C.NVM_SUCCESS
+}
+
+func recordTransferFailureEvent(errNo int, from string, to string, value string,
+ height uint64, wsState WorldState, txHash byteutils.Hash) {
+
+ if errNo == TransferFuncSuccess && height > core.TransferFromContractEventRecordableHeight {
+ event := &TransferFromContractEvent{
+ Amount: value,
+ From: from,
+ To: to,
+ }
+ eData, err := json.Marshal(event)
+ if err != nil {
+ logging.VLog().WithFields(logrus.Fields{
+ "from": from,
+ "to": to,
+ "amount": value,
+ "err": err,
+ }).Fatal("failed to marshal TransferFromContractEvent")
+ }
+ wsState.RecordEvent(txHash, &state.Event{Topic: core.TopicTransferFromContract, Data: string(eData)})
+
+ } else if height >= core.TransferFromContractFailureEventRecordableHeight {
+ var errMsg string
+ switch errNo {
+ case TransferFuncSuccess:
+ errMsg = ""
+ case TransferAddressParseErr:
+ errMsg = "failed to parse to address"
+ case TransferStringToBigIntErr:
+ errMsg = "failed to parse transfer amount"
+ case TransferSubBalance:
+ errMsg = "failed to sub balace from contract address"
+ default:
+ logging.VLog().WithFields(logrus.Fields{
+ "from": from,
+ "to": to,
+ "amount": value,
+ "errNo": errNo,
+ }).Fatal("failed to marshal TransferFromContractEvent")
+ }
+
+ status := uint8(1)
+ if errNo != TransferFuncSuccess {
+ status = 0
+ }
+
+ event := &TransferFromContractFailureEvent{
+ Amount: value,
+ From: from,
+ To: to,
+ Status: status,
+ Error: errMsg,
+ }
+
+ eData, err := json.Marshal(event)
+ if err != nil {
+ logging.VLog().WithFields(logrus.Fields{
+ "from": from,
+ "to": to,
+ "amount": value,
+ "status": event.Status,
+ "error": err,
+ }).Fatal("failed to marshal TransferFromContractEvent")
+ }
+
+ wsState.RecordEvent(txHash, &state.Event{Topic: core.TopicTransferFromContract, Data: string(eData)})
+
}
- return C.CString(string(json))
}
// TransferFunc transfer vale to address
//export TransferFunc
func TransferFunc(handler unsafe.Pointer, to *C.char, v *C.char, gasCnt *C.size_t) int {
engine, _ := getEngineByStorageHandler(uint64(uintptr(handler)))
- if engine == nil || engine.ctx.block == nil {
- logging.VLog().Error("Failed to get engine.")
- return TransferGetEngineErr
+ if engine == nil || engine.ctx == nil || engine.ctx.block == nil ||
+ engine.ctx.state == nil || engine.ctx.tx == nil {
+ logging.VLog().Fatal("Unexpected error: failed to get engine.")
+ }
+
+ wsState := engine.ctx.state
+ height := engine.ctx.block.Height()
+ txHash := engine.ctx.tx.Hash()
+
+ cAddr, err := core.AddressParseFromBytes(engine.ctx.contract.Address())
+ if err != nil {
+ logging.VLog().WithFields(logrus.Fields{
+ "txhash": engine.ctx.tx.Hash().String(),
+ "address": engine.ctx.contract.Address(),
+ "err": err,
+ }).Fatal("Unexpected error: failed to parse contract address")
}
// calculate Gas.
- *gasCnt = C.size_t(2000)
+ *gasCnt = C.size_t(TransferGasBase)
addr, err := core.AddressParse(C.GoString(to))
if err != nil {
logging.VLog().WithFields(logrus.Fields{
- "handler": uint64(uintptr(handler)),
- "key": C.GoString(to),
+ "handler": uint64(uintptr(handler)),
+ "toAddress": C.GoString(to),
}).Debug("TransferFunc parse address failed.")
+ recordTransferFailureEvent(TransferAddressParseErr, cAddr.String(), "", "", height, wsState, txHash)
return TransferAddressParseErr
}
@@ -143,8 +239,7 @@ func TransferFunc(handler unsafe.Pointer, to *C.char, v *C.char, gasCnt *C.size_
"handler": uint64(uintptr(handler)),
"address": addr,
"err": err,
- }).Debug("GetAccountStateFunc get account state failed.")
- return TransferGetAccountErr
+ }).Fatal("GetAccountStateFunc get account state failed.")
}
amount, err := util.NewUint128FromString(C.GoString(v))
@@ -154,9 +249,9 @@ func TransferFunc(handler unsafe.Pointer, to *C.char, v *C.char, gasCnt *C.size_
"address": addr,
"err": err,
}).Debug("GetAmountFunc get amount failed.")
+ recordTransferFailureEvent(TransferStringToBigIntErr, cAddr.String(), addr.String(), "", height, wsState, txHash)
return TransferStringToBigIntErr
}
-
// update balance
if amount.Cmp(util.NewUint128()) > 0 {
err = engine.ctx.contract.SubBalance(amount)
@@ -166,6 +261,7 @@ func TransferFunc(handler unsafe.Pointer, to *C.char, v *C.char, gasCnt *C.size_
"key": C.GoString(to),
"err": err,
}).Debug("TransferFunc SubBalance failed.")
+ recordTransferFailureEvent(TransferSubBalance, cAddr.String(), addr.String(), amount.String(), height, wsState, txHash)
return TransferSubBalance
}
@@ -176,42 +272,13 @@ func TransferFunc(handler unsafe.Pointer, to *C.char, v *C.char, gasCnt *C.size_
"amount": amount,
"address": addr,
"err": err,
- }).Debug("failed to add balance")
- return TransferAddBalance
+ }).Fatal("failed to add balance")
+ // recordTransferFailureEvent(TransferSubBalance, cAddr.String(), addr.String(), amount.String(), height, wsState, txHash)
+ // return TransferAddBalance
}
}
- if engine.ctx.block.Height() >= core.TransferFromContractEventRecordableHeight {
- cAddr, err := core.AddressParseFromBytes(engine.ctx.contract.Address())
- if err != nil {
- logging.VLog().WithFields(logrus.Fields{
- "txhash": engine.ctx.tx.Hash().String(),
- "address": engine.ctx.contract.Address(),
- "err": err,
- }).Debug("failed to parse contract address")
- return TransferAddressFailed
- }
-
- event := &TransferFromContractEvent{
- Amount: amount.String(),
- From: cAddr.String(),
- To: addr.String(),
- }
-
- eData, err := json.Marshal(event)
- if err != nil {
- logging.VLog().WithFields(logrus.Fields{
- "from": cAddr.String(),
- "to": addr.String(),
- "amount": amount.String(),
- "err": err,
- }).Debug("failed to marshal TransferFromContractEvent")
- return TransferRecordEventFailed
- }
-
- engine.ctx.state.RecordEvent(engine.ctx.tx.Hash(), &state.Event{Topic: core.TopicTransferFromContract, Data: string(eData)})
- }
-
+ recordTransferFailureEvent(TransferFuncSuccess, cAddr.String(), addr.String(), amount.String(), height, wsState, txHash)
return TransferFuncSuccess
}
@@ -219,7 +286,7 @@ func TransferFunc(handler unsafe.Pointer, to *C.char, v *C.char, gasCnt *C.size_
//export VerifyAddressFunc
func VerifyAddressFunc(handler unsafe.Pointer, address *C.char, gasCnt *C.size_t) int {
// calculate Gas.
- *gasCnt = C.size_t(100)
+ *gasCnt = C.size_t(VerifyAddressGasBase)
addr, err := core.AddressParse(C.GoString(address))
if err != nil {
@@ -227,3 +294,131 @@ func VerifyAddressFunc(handler unsafe.Pointer, address *C.char, gasCnt *C.size_t
}
return int(addr.Type())
}
+
+// GetPreBlockHashFunc returns hash of the block before current tail by n
+//export GetPreBlockHashFunc
+func GetPreBlockHashFunc(handler unsafe.Pointer, offset C.ulonglong,
+ gasCnt *C.size_t, result **C.char, exceptionInfo **C.char) int {
+ *result = nil
+ *exceptionInfo = nil
+ n := uint64(offset)
+ if n > uint64(maxBlockOffset) { //31 days
+ *exceptionInfo = C.CString("Blockchain.GetPreBlockHash(), argument out of range")
+ return C.NVM_EXCEPTION_ERR
+ }
+
+ engine, _ := getEngineByStorageHandler(uint64(uintptr(handler)))
+ if engine == nil || engine.ctx == nil || engine.ctx.block == nil || engine.ctx.state == nil {
+ logging.VLog().Error("Unexpected error: failed to get engine.")
+ return C.NVM_UNEXPECTED_ERR
+ }
+ wsState := engine.ctx.state
+ // calculate Gas.
+ *gasCnt = C.size_t(GetPreBlockHashGasBase)
+
+ //get height
+ height := engine.ctx.block.Height()
+ if n >= height { // have checked it in lib js
+ logging.VLog().WithFields(logrus.Fields{
+ "height": height,
+ "offset": n,
+ }).Debug("offset is large than height")
+ *exceptionInfo = C.CString("Blockchain.GetPreBlockHash(), argument[offset] is large than current height")
+ return C.NVM_EXCEPTION_ERR
+ }
+ height -= n
+
+ blockHash, err := wsState.GetBlockHashByHeight(height)
+ if err != nil {
+ logging.VLog().WithFields(logrus.Fields{
+ "height": height,
+ "err": err,
+ }).Error("Unexpected error: Failed to get block hash from wsState by height")
+ return C.NVM_UNEXPECTED_ERR
+ }
+
+ *result = C.CString(byteutils.Hex(blockHash))
+ return C.NVM_SUCCESS
+}
+
+// GetPreBlockSeedFunc returns hash of the block before current tail by n
+//export GetPreBlockSeedFunc
+func GetPreBlockSeedFunc(handler unsafe.Pointer, offset C.ulonglong,
+ gasCnt *C.size_t, result **C.char, exceptionInfo **C.char) int {
+ *result = nil
+ *exceptionInfo = nil
+
+ n := uint64(offset)
+ if n > uint64(maxBlockOffset) { //31 days
+ *exceptionInfo = C.CString("Blockchain.GetPreBlockSeed(), argument out of range")
+ return C.NVM_EXCEPTION_ERR
+ }
+
+ engine, _ := getEngineByStorageHandler(uint64(uintptr(handler)))
+ if engine == nil || engine.ctx == nil || engine.ctx.block == nil || engine.ctx.state == nil {
+ logging.VLog().Error("Unexpected error: failed to get engine")
+ return C.NVM_UNEXPECTED_ERR
+ }
+ wsState := engine.ctx.state
+ // calculate Gas.
+ *gasCnt = C.size_t(GetPreBlockSeedGasBase)
+
+ //get height
+ height := engine.ctx.block.Height()
+ if n >= height { // have checked it in lib js
+ logging.VLog().WithFields(logrus.Fields{
+ "height": height,
+ "offset": n,
+ }).Debug("offset is large than height")
+ *exceptionInfo = C.CString("Blockchain.GetPreBlockSeed(), argument[offset] is large than current height")
+ return C.NVM_EXCEPTION_ERR
+ }
+
+ height -= n
+ if height < core.RandomAvailableHeight {
+ *exceptionInfo = C.CString("Blockchain.GetPreBlockSeed(), seed is not available at this height")
+ return C.NVM_EXCEPTION_ERR
+ }
+
+ blockHash, err := wsState.GetBlockHashByHeight(height)
+ if err != nil {
+ logging.VLog().WithFields(logrus.Fields{
+ "height": height,
+ "err": err,
+ "blockHash": blockHash,
+ }).Error("Unexpected error: Failed to get block hash from wsState by height")
+ return C.NVM_UNEXPECTED_ERR
+ }
+
+ bytes, err := wsState.GetBlock(blockHash)
+ if err != nil {
+ logging.VLog().WithFields(logrus.Fields{
+ "height": height,
+ "err": err,
+ "blockHash": blockHash,
+ }).Error("Unexpected error: Failed to get block from wsState by hash")
+ return C.NVM_UNEXPECTED_ERR
+ }
+
+ pbBlock := new(corepb.Block)
+ if err = proto.Unmarshal(bytes, pbBlock); err != nil {
+ logging.VLog().WithFields(logrus.Fields{
+ "bytes": bytes,
+ "height": height,
+ "err": err,
+ }).Error("Unexpected error: Failed to unmarshal pbBlock")
+ return C.NVM_UNEXPECTED_ERR
+ }
+
+ if pbBlock.GetHeader() == nil || pbBlock.GetHeader().GetRandom() == nil ||
+ pbBlock.GetHeader().GetRandom().GetVrfSeed() == nil {
+ logging.VLog().WithFields(logrus.Fields{
+ "pbBlock": pbBlock,
+ "height": height,
+ }).Error("Unexpected error: No random found in block header")
+ return C.NVM_UNEXPECTED_ERR
+ }
+
+ *result = C.CString(byteutils.Hex(pbBlock.GetHeader().GetRandom().GetVrfSeed()))
+ return C.NVM_SUCCESS
+}
diff --git a/nf/nvm/cfuncs.go b/nf/nvm/cfuncs.go
index 40812bf43..1bd279893 100644
--- a/nf/nvm/cfuncs.go
+++ b/nf/nvm/cfuncs.go
@@ -26,6 +26,7 @@ void V8Log(int level, const char *msg);
// require.
char *RequireDelegateFunc(void *handler, const char *filename, size_t *lineOffset);
+char *AttachLibVersionDelegateFunc(void *handler, const char *libname);
// storage.
char *StorageGetFunc(void *handler, const char *key, size_t *gasCnt);
@@ -34,13 +35,23 @@ int StorageDelFunc(void *handler, const char *key, size_t *gasCnt);
// blockchain.
char *GetTxByHashFunc(void *handler, const char *hash, size_t *gasCnt);
-char *GetAccountStateFunc(void *handler, const char *address, size_t *gasCnt);
+int GetAccountStateFunc(void *handler, const char *address, size_t *gasCnt, char **result, char **info);
int TransferFunc(void *handler, const char *to, const char *value, size_t *gasCnt);
int VerifyAddressFunc(void *handler, const char *address, size_t *gasCnt);
+int GetPreBlockHashFunc(void *handler, unsigned long long offset, size_t *gasCnt, char **result, char **info);
+int GetPreBlockSeedFunc(void *handler, unsigned long long offset, size_t *gasCnt, char **result, char **info);
// event.
void EventTriggerFunc(void *handler, const char *topic, const char *data, size_t *gasCnt);
+// crypto
+char *Sha256Func(const char *data, size_t *gasCnt);
+char *Sha3256Func(const char *data, size_t *gasCnt);
+char *Ripemd160Func(const char *data, size_t *gasCnt);
+char *RecoverAddressFunc(int alg, const char *data, const char *sign, size_t *gasCnt);
+char *Md5Func(const char *data, size_t *gasCnt);
+char *Base64Func(const char *data, size_t *gasCnt);
+
// The gateway functions.
void V8Log_cgo(int level, const char *msg) {
V8Log(level, msg);
@@ -50,6 +61,10 @@ char *RequireDelegateFunc_cgo(void *handler, const char *filename, size_t *lineO
return RequireDelegateFunc(handler, filename, lineOffset);
}
+char *AttachLibVersionDelegateFunc_cgo(void *handler, const char *libname) {
+ return AttachLibVersionDelegateFunc(handler, libname);
+}
+
char *StorageGetFunc_cgo(void *handler, const char *key, size_t *gasCnt) {
return StorageGetFunc(handler, key, gasCnt);
};
@@ -63,8 +78,8 @@ int StorageDelFunc_cgo(void *handler, const char *key, size_t *gasCnt) {
char *GetTxByHashFunc_cgo(void *handler, const char *hash, size_t *gasCnt) {
return GetTxByHashFunc(handler, hash, gasCnt);
};
-char *GetAccountStateFunc_cgo(void *handler, const char *address, size_t *gasCnt) {
- return GetAccountStateFunc(handler, address, gasCnt);
+int GetAccountStateFunc_cgo(void *handler, const char *address, size_t *gasCnt, char **result, char **info) {
+ return GetAccountStateFunc(handler, address, gasCnt, result, info);
};
int TransferFunc_cgo(void *handler, const char *to, const char *value, size_t *gasCnt) {
return TransferFunc(handler, to, value, gasCnt);
@@ -73,9 +88,36 @@ int VerifyAddressFunc_cgo(void *handler, const char *address, size_t *gasCnt) {
return VerifyAddressFunc(handler, address, gasCnt);
};
+int GetPreBlockHashFunc_cgo(void *handler, unsigned long long offset, size_t *gasCnt, char **result, char **info) {
+ return GetPreBlockHashFunc(handler, offset, gasCnt, result, info);
+}
+
+int GetPreBlockSeedFunc_cgo(void *handler, unsigned long long offset, size_t *gasCnt, char **result, char **info) {
+ return GetPreBlockSeedFunc(handler, offset, gasCnt, result, info);
+}
+
void EventTriggerFunc_cgo(void *handler, const char *topic, const char *data, size_t *gasCnt) {
EventTriggerFunc(handler, topic, data, gasCnt);
};
+char *Sha256Func_cgo(const char *data, size_t *gasCnt) {
+ return Sha256Func(data, gasCnt);
+}
+char *Sha3256Func_cgo(const char *data, size_t *gasCnt) {
+ return Sha3256Func(data, gasCnt);
+}
+char *Ripemd160Func_cgo(const char *data, size_t *gasCnt) {
+ return Ripemd160Func(data, gasCnt);
+}
+char *RecoverAddressFunc_cgo(int alg, const char *data, const char *sign, size_t *gasCnt) {
+ return RecoverAddressFunc(alg, data, sign, gasCnt);
+}
+char *Md5Func_cgo(const char *data, size_t *gasCnt) {
+ return Md5Func(data, gasCnt);
+}
+char *Base64Func_cgo(const char *data, size_t *gasCnt) {
+ return Base64Func(data, gasCnt);
+}
+
*/
import "C"
diff --git a/nf/nvm/crypto.go b/nf/nvm/crypto.go
new file mode 100644
index 000000000..a6928cb9a
--- /dev/null
+++ b/nf/nvm/crypto.go
@@ -0,0 +1,124 @@
+// Copyright (C) 2018 go-nebulas authors
+//
+// This file is part of the go-nebulas library.
+//
+// the go-nebulas library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU 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-nebulas 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with the go-nebulas library. If not, see .
+//
+
+package nvm
+
+import "C"
+
+import (
+ "crypto/md5"
+
+ "github.com/nebulasio/go-nebulas/core"
+ "github.com/nebulasio/go-nebulas/crypto/hash"
+ "github.com/nebulasio/go-nebulas/crypto/keystore"
+ "github.com/nebulasio/go-nebulas/util/byteutils"
+ "github.com/nebulasio/go-nebulas/util/logging"
+ "github.com/sirupsen/logrus"
+)
+
+// Sha256Func ..
+//export Sha256Func
+func Sha256Func(data *C.char, gasCnt *C.size_t) *C.char {
+ s := C.GoString(data)
+ *gasCnt = C.size_t(len(s) + CryptoSha256GasBase)
+
+ r := hash.Sha256([]byte(s))
+ return C.CString(byteutils.Hex(r))
+}
+
+// Sha3256Func ..
+//export Sha3256Func
+func Sha3256Func(data *C.char, gasCnt *C.size_t) *C.char {
+ s := C.GoString(data)
+ *gasCnt = C.size_t(len(s) + CryptoSha3256GasBase)
+
+ r := hash.Sha3256([]byte(s))
+ return C.CString(byteutils.Hex(r))
+}
+
+// Ripemd160Func ..
+//export Ripemd160Func
+func Ripemd160Func(data *C.char, gasCnt *C.size_t) *C.char {
+ s := C.GoString(data)
+ *gasCnt = C.size_t(len(s) + CryptoRipemd160GasBase)
+
+ r := hash.Ripemd160([]byte(s))
+ return C.CString(byteutils.Hex(r))
+}
+
+// RecoverAddressFunc ..
+//export RecoverAddressFunc
+func RecoverAddressFunc(alg int, data, sign *C.char, gasCnt *C.size_t) *C.char {
+ d := C.GoString(data)
+ s := C.GoString(sign)
+
+ *gasCnt = C.size_t(CryptoRecoverAddressGasBase)
+
+ plain, err := byteutils.FromHex(d)
+ if err != nil {
+ logging.VLog().WithFields(logrus.Fields{
+ "hash": d,
+ "sign": s,
+ "alg": alg,
+ "err": err,
+ }).Debug("convert hash to byte array error.")
+ return nil
+ }
+ cipher, err := byteutils.FromHex(s)
+ if err != nil {
+ logging.VLog().WithFields(logrus.Fields{
+ "data": d,
+ "sign": s,
+ "alg": alg,
+ "err": err,
+ }).Debug("convert sign to byte array error.")
+ return nil
+ }
+ addr, err := core.RecoverSignerFromSignature(keystore.Algorithm(alg), plain, cipher)
+ if err != nil {
+ logging.VLog().WithFields(logrus.Fields{
+ "data": d,
+ "sign": s,
+ "alg": alg,
+ "err": err,
+ }).Debug("recover address error.")
+ return nil
+ }
+
+ return C.CString(addr.String())
+}
+
+// Md5Func ..
+//export Md5Func
+func Md5Func(data *C.char, gasCnt *C.size_t) *C.char {
+ s := C.GoString(data)
+ *gasCnt = C.size_t(len(s) + CryptoMd5GasBase)
+
+ r := md5.Sum([]byte(s))
+ return C.CString(byteutils.Hex(r[:]))
+}
+
+// Base64Func ..
+//export Base64Func
+func Base64Func(data *C.char, gasCnt *C.size_t) *C.char {
+ s := C.GoString(data)
+ *gasCnt = C.size_t(len(s) + CryptoBase64GasBase)
+
+ r := hash.Base64Encode([]byte(s))
+ return C.CString(string(r))
+}
diff --git a/nf/nvm/engine.go b/nf/nvm/engine.go
index b6f10f40c..cb8b82cb7 100644
--- a/nf/nvm/engine.go
+++ b/nf/nvm/engine.go
@@ -42,7 +42,12 @@ func (nvm *NebulasVM) CreateEngine(block *core.Block, tx *core.Transaction, cont
// CheckV8Run to check V8 env is OK
func (nvm *NebulasVM) CheckV8Run() error {
- engine := NewV8Engine(nil)
+ engine := NewV8Engine(&Context{
+ block: core.MockBlock(nil, 1),
+ contract: state.MockAccount("1.0.0"),
+ tx: nil,
+ state: nil,
+ })
_, err := engine.RunScriptSource("", 0)
engine.Dispose()
return err
diff --git a/nf/nvm/engine_v8.go b/nf/nvm/engine_v8.go
index 3249e22a2..b5683ebe2 100644
--- a/nf/nvm/engine_v8.go
+++ b/nf/nvm/engine_v8.go
@@ -29,6 +29,7 @@ package nvm
void V8Log_cgo(int level, const char *msg);
char *RequireDelegateFunc_cgo(void *handler, const char *filename, size_t *lineOffset);
+char *AttachLibVersionDelegateFunc_cgo(void *handler, const char *libname);
char *StorageGetFunc_cgo(void *handler, const char *key, size_t *gasCnt);
int StoragePutFunc_cgo(void *handler, const char *key, const char *value, size_t *gasCnt);
@@ -38,6 +39,15 @@ char *GetTxByHashFunc_cgo(void *handler, const char *hash);
char *GetAccountStateFunc_cgo(void *handler, const char *address);
int TransferFunc_cgo(void *handler, const char *to, const char *value);
int VerifyAddressFunc_cgo(void *handler, const char *address);
+char *GetPreBlockHashFunc_cgo(void *handler, unsigned long long offset, size_t *gasCnt);
+char *GetPreBlockSeedFunc_cgo(void *handler, unsigned long long offset, size_t *gasCnt);
+
+char *Sha256Func_cgo(const char *data, size_t *gasCnt);
+char *Sha3256Func_cgo(const char *data, size_t *gasCnt);
+char *Ripemd160Func_cgo(const char *data, size_t *gasCnt);
+char *RecoverAddressFunc_cgo(int alg, const char *data, const char *sign, size_t *gasCnt);
+char *Md5Func_cgo(const char *data, size_t *gasCnt);
+char *Base64Func_cgo(const char *data, size_t *gasCnt);
void EventTriggerFunc_cgo(void *handler, const char *topic, const char *data, size_t *gasCnt);
@@ -59,13 +69,13 @@ import (
"github.com/sirupsen/logrus"
)
-const (
- // ExecutionTimeoutInSeconds max v8 execution timeout.
- ExecutionTimeoutInSeconds = 5
-)
const (
ExecutionFailedErr = 1
ExecutionTimeOutErr = 2
+
+ // ExecutionTimeoutInSeconds max v8 execution timeout.
+ ExecutionTimeoutInSeconds = 5
+ TimeoutGasLimitCost = 100000000
)
//engine_v8 private data
@@ -111,16 +121,33 @@ func InitV8Engine() {
C.InitializeLogger((C.LogFunc)(unsafe.Pointer(C.V8Log_cgo)))
// Require.
- C.InitializeRequireDelegate((C.RequireDelegate)(unsafe.Pointer(C.RequireDelegateFunc_cgo)))
+ C.InitializeRequireDelegate((C.RequireDelegate)(unsafe.Pointer(C.RequireDelegateFunc_cgo)), (C.AttachLibVersionDelegate)(unsafe.Pointer(C.AttachLibVersionDelegateFunc_cgo)))
+
+ // execution_env require
+ C.InitializeExecutionEnvDelegate((C.AttachLibVersionDelegate)(unsafe.Pointer(C.AttachLibVersionDelegateFunc_cgo)))
// Storage.
C.InitializeStorage((C.StorageGetFunc)(unsafe.Pointer(C.StorageGetFunc_cgo)), (C.StoragePutFunc)(unsafe.Pointer(C.StoragePutFunc_cgo)), (C.StorageDelFunc)(unsafe.Pointer(C.StorageDelFunc_cgo)))
// Blockchain.
- C.InitializeBlockchain((C.GetTxByHashFunc)(unsafe.Pointer(C.GetTxByHashFunc_cgo)), (C.GetAccountStateFunc)(unsafe.Pointer(C.GetAccountStateFunc_cgo)), (C.TransferFunc)(unsafe.Pointer(C.TransferFunc_cgo)), (C.VerifyAddressFunc)(unsafe.Pointer(C.VerifyAddressFunc_cgo)))
+ C.InitializeBlockchain((C.GetTxByHashFunc)(unsafe.Pointer(C.GetTxByHashFunc_cgo)),
+ (C.GetAccountStateFunc)(unsafe.Pointer(C.GetAccountStateFunc_cgo)),
+ (C.TransferFunc)(unsafe.Pointer(C.TransferFunc_cgo)),
+ (C.VerifyAddressFunc)(unsafe.Pointer(C.VerifyAddressFunc_cgo)),
+ (C.GetPreBlockHashFunc)(unsafe.Pointer(C.GetPreBlockHashFunc_cgo)),
+ (C.GetPreBlockSeedFunc)(unsafe.Pointer(C.GetPreBlockSeedFunc_cgo)),
+ )
// Event.
C.InitializeEvent((C.EventTriggerFunc)(unsafe.Pointer(C.EventTriggerFunc_cgo)))
+
+ // Crypto
+ C.InitializeCrypto((C.Sha256Func)(unsafe.Pointer(C.Sha256Func_cgo)),
+ (C.Sha3256Func)(unsafe.Pointer(C.Sha3256Func_cgo)),
+ (C.Ripemd160Func)(unsafe.Pointer(C.Ripemd160Func_cgo)),
+ (C.RecoverAddressFunc)(unsafe.Pointer(C.RecoverAddressFunc_cgo)),
+ (C.Md5Func)(unsafe.Pointer(C.Md5Func_cgo)),
+ (C.Base64Func)(unsafe.Pointer(C.Base64Func_cgo)))
}
// DisposeV8Engine dispose the v8 engine.
@@ -203,6 +230,10 @@ func (e *V8Engine) SetTestingFlag(flag bool) {
}*/
}
+func (e *V8Engine) SetTimeOut(timeout uint64) {
+ e.v8engine.timeout = C.int(timeout) //TODO:
+}
+
// SetExecutionLimits set execution limits of V8 Engine, prevent Halting Problem.
func (e *V8Engine) SetExecutionLimits(limitsOfExecutionInstructions, limitsOfTotalMemorySize uint64) error {
e.v8engine.limits_of_executed_instructions = C.size_t(limitsOfExecutionInstructions)
@@ -294,33 +325,51 @@ func (e *V8Engine) RunScriptSource(source string, sourceLineOffset int) (string,
// }()
ret = C.RunScriptSourceThread(&cResult, e.v8engine, cSource, C.int(sourceLineOffset), C.uintptr_t(e.lcsHandler),
C.uintptr_t(e.gcsHandler))
+ e.CollectTracingStats()
- if ret == ExecutionTimeOutErr {
+ //set err
+ if ret == C.NVM_EXE_TIMEOUT_ERR {
err = ErrExecutionTimeout
- } else if ret == ExecutionFailedErr {
- err = core.ErrExecutionFailed
+ ctx := e.Context()
+ if ctx == nil || ctx.block == nil {
+ logging.VLog().WithFields(logrus.Fields{
+ "err": err,
+ "ctx": ctx,
+ }).Error("Unexpected: Failed to get current height")
+ err = core.ErrUnexpected
+ } else if ctx.block.Height() >= core.NewNvmExeTimeoutConsumeGasHeight {
+ if TimeoutGasLimitCost > e.limitsOfExecutionInstructions {
+ e.actualCountOfExecutionInstructions = e.limitsOfExecutionInstructions
+ } else {
+ e.actualCountOfExecutionInstructions = TimeoutGasLimitCost
+ }
+ }
+ } else if ret == C.NVM_UNEXPECTED_ERR {
+ err = core.ErrUnexpected
+ } else {
+ if ret != C.NVM_SUCCESS {
+ err = core.ErrExecutionFailed
+ }
+ if e.limitsOfExecutionInstructions > 0 &&
+ e.limitsOfExecutionInstructions < e.actualCountOfExecutionInstructions {
+ // Reach instruction limits.
+ err = ErrInsufficientGas
+ e.actualCountOfExecutionInstructions = e.limitsOfExecutionInstructions
+ } else if e.limitsOfTotalMemorySize > 0 && e.limitsOfTotalMemorySize < e.actualTotalMemorySize {
+ // reach memory limits.
+ err = ErrExceedMemoryLimits
+ e.actualCountOfExecutionInstructions = e.limitsOfExecutionInstructions
+ }
}
+ //set result
if cResult != nil {
result = C.GoString(cResult)
C.free(unsafe.Pointer(cResult))
- } else if ret == 0 {
+ } else if ret == C.NVM_SUCCESS {
result = "\"\"" // default JSON String.
}
- // collect tracing stats.
- e.CollectTracingStats()
- if e.limitsOfExecutionInstructions > 0 && e.limitsOfExecutionInstructions < e.actualCountOfExecutionInstructions {
- // Reach instruction limits.
- err = ErrInsufficientGas
- } else if e.limitsOfTotalMemorySize > 0 && e.limitsOfTotalMemorySize < e.actualTotalMemorySize {
- // reach memory limits.
- err = ErrExceedMemoryLimits
- }
- if e.actualCountOfExecutionInstructions > e.limitsOfExecutionInstructions || err == ErrExceedMemoryLimits { //ToDo ErrExceedMemoryLimits value is same in each linux
- e.actualCountOfExecutionInstructions = e.limitsOfExecutionInstructions //ToDo memory pass whether exhaust ?
- }
-
return result, err
}
diff --git a/nf/nvm/engine_v8_test.go b/nf/nvm/engine_v8_test.go
index cb7bcf012..5b8a65f0f 100644
--- a/nf/nvm/engine_v8_test.go
+++ b/nf/nvm/engine_v8_test.go
@@ -33,11 +33,10 @@ import (
"github.com/gogo/protobuf/proto"
"github.com/nebulasio/go-nebulas/account"
- "github.com/nebulasio/go-nebulas/net"
-
"github.com/nebulasio/go-nebulas/consensus/dpos"
"github.com/nebulasio/go-nebulas/core/pb"
"github.com/nebulasio/go-nebulas/neblet/pb"
+ "github.com/nebulasio/go-nebulas/net"
"github.com/nebulasio/go-nebulas/core"
"github.com/nebulasio/go-nebulas/core/state"
@@ -57,6 +56,7 @@ func newUint128FromIntWrapper(a int64) *util.Uint128 {
}
type testBlock struct {
+ height uint64
}
// Coinbase mock
@@ -72,7 +72,7 @@ func (block *testBlock) Hash() byteutils.Hash {
// Height mock
func (block *testBlock) Height() uint64 {
- return core.NvmMemoryLimitWithoutInjectHeight
+ return block.height
}
// RandomSeed mock
@@ -105,7 +105,12 @@ func (block *testBlock) Timestamp() int64 {
}
func mockBlock() Block {
- block := &testBlock{}
+ block := &testBlock{core.NvmMemoryLimitWithoutInjectHeight}
+ return block
+}
+
+func mockBlockForLib(height uint64) Block {
+ block := &testBlock{height}
return block
}
@@ -161,7 +166,7 @@ func TestRunScriptSource(t *testing.T) {
owner, err := context.GetOrCreateUserAccount([]byte("account1"))
assert.Nil(t, err)
owner.AddBalance(newUint128FromIntWrapper(1000000000))
- contract, _ := context.CreateContractAccount([]byte("account2"), nil)
+ contract, _ := context.CreateContractAccount([]byte("account2"), nil, nil)
ctx, err := NewContext(mockBlock(), mockTransaction(), contract, context)
engine := NewV8Engine(ctx)
@@ -200,7 +205,7 @@ func TestRunScriptSourceInModule(t *testing.T) {
owner, err := context.GetOrCreateUserAccount([]byte("account1"))
assert.Nil(t, err)
owner.AddBalance(newUint128FromIntWrapper(1000000000))
- contract, _ := context.CreateContractAccount([]byte("account2"), nil)
+ contract, _ := context.CreateContractAccount([]byte("account2"), nil, nil)
ctx, err := NewContext(mockBlock(), mockTransaction(), contract, context)
engine := NewV8Engine(ctx)
@@ -244,7 +249,7 @@ func TestRunScriptSourceWithLimits(t *testing.T) {
owner, err := context.GetOrCreateUserAccount([]byte("account1"))
assert.Nil(t, err)
owner.AddBalance(newUint128FromIntWrapper(100000))
- contract, _ := context.CreateContractAccount([]byte("account2"), nil)
+ contract, _ := context.CreateContractAccount([]byte("account2"), nil, nil)
ctx, err := NewContext(mockBlock(), mockTransaction(), contract, context)
// direct run.
@@ -299,7 +304,7 @@ func TestV8ResourceLimit(t *testing.T) {
owner, err := context.GetOrCreateUserAccount([]byte("account1"))
assert.Nil(t, err)
owner.AddBalance(newUint128FromIntWrapper(10000000))
- contract, _ := context.CreateContractAccount([]byte("account2"), nil)
+ contract, _ := context.CreateContractAccount([]byte("account2"), nil, nil)
ctx, err := NewContext(mockBlock(), mockTransaction(), contract, context)
engine := NewV8Engine(ctx)
@@ -351,7 +356,7 @@ func TestRunScriptSourceTimeout(t *testing.T) {
// owner, err := context.GetOrCreateUserAccount([]byte("account1"))
// assert.Nil(t, err)
- contract, _ := context.CreateContractAccount([]byte("account2"), nil)
+ contract, _ := context.CreateContractAccount([]byte("account2"), nil, nil)
ctx, err := NewContext(mockBlock(), mockTransaction(), contract, context)
// direct run.
@@ -398,7 +403,7 @@ func TestDeployAndInitAndCall(t *testing.T) {
owner, err := context.GetOrCreateUserAccount([]byte("account1"))
assert.Nil(t, err)
owner.AddBalance(newUint128FromIntWrapper(10000000))
- contract, _ := context.CreateContractAccount([]byte("account2"), nil)
+ contract, _ := context.CreateContractAccount([]byte("account2"), nil, nil)
ctx, err := NewContext(mockBlock(), mockTransaction(), contract, context)
engine := NewV8Engine(ctx)
@@ -424,7 +429,7 @@ func TestDeployAndInitAndCall(t *testing.T) {
context, _ = state.NewWorldState(dpos.NewDpos(), mem)
owner, err = context.GetOrCreateUserAccount([]byte("account1"))
assert.Nil(t, err)
- contract, err = context.CreateContractAccount([]byte("account2"), nil)
+ contract, err = context.CreateContractAccount([]byte("account2"), nil, nil)
assert.Nil(t, err)
ctx, err = NewContext(mockBlock(), mockTransaction(), contract, context)
@@ -459,7 +464,7 @@ func TestERC20(t *testing.T) {
owner, err := context.GetOrCreateUserAccount([]byte("account1"))
assert.Nil(t, err)
owner.AddBalance(newUint128FromIntWrapper(10000000))
- contract, _ := context.CreateContractAccount([]byte("account2"), nil)
+ contract, _ := context.CreateContractAccount([]byte("account2"), nil, nil)
ctx, err := NewContext(mockBlock(), mockTransaction(), contract, context)
engine := NewV8Engine(ctx)
@@ -519,7 +524,7 @@ func TestContracts(t *testing.T) {
owner, err := context.GetOrCreateUserAccount([]byte("account1"))
assert.Nil(t, err)
owner.AddBalance(newUint128FromIntWrapper(10000000))
- contract, err := context.CreateContractAccount([]byte("account2"), nil)
+ contract, err := context.CreateContractAccount([]byte("account2"), nil, nil)
assert.Nil(t, err)
ctx, err := NewContext(mockBlock(), mockTransaction(), contract, context)
@@ -542,6 +547,158 @@ func TestContracts(t *testing.T) {
}
}
+func TestContractFeatureGetAccountState(t *testing.T) {
+ type fields struct {
+ function string
+ args string
+ result string
+ error string
+ }
+ tests := []struct {
+ contract string
+ sourceType string
+ initArgs string
+ calls []fields
+ }{
+ {
+ "./test/test_contract_features.js",
+ "js",
+ "[]",
+ []fields{
+ {"testGetAccountState", "[]", "\"1000000000000\"", ""},
+ {"testGetAccountStateWrongAddr", "[]", "\"0\"", ""},
+ },
+ },
+ }
+
+ account1 := "n1FkntVUMPAsESuCAAPK711omQk19JotBjM"
+ account2 := "n1JNHZJEUvfBYfjDRD14Q73FX62nJAzXkMR"
+
+ for _, tt := range tests {
+ t.Run(tt.contract, func(t *testing.T) {
+ data, err := ioutil.ReadFile(tt.contract)
+ assert.Nil(t, err, "contract path read error")
+
+ mem, _ := storage.NewMemoryStorage()
+ context, _ := state.NewWorldState(dpos.NewDpos(), mem)
+ add1, _ := core.AddressParse(account1)
+ owner, err := context.GetOrCreateUserAccount(add1.Bytes())
+ assert.Nil(t, err)
+ owner.AddBalance(newUint128FromIntWrapper(1000000000000))
+ add2, _ := core.AddressParse(account2)
+ contract, err := context.CreateContractAccount(add2.Bytes(), nil, &corepb.ContractMeta{Version: "1.0.5"})
+ assert.Nil(t, err)
+ tx := mockNormalTransaction("n1FkntVUMPAsESuCAAPK711omQk19JotBjM", "n1JNHZJEUvfBYfjDRD14Q73FX62nJAzXkMR", "0")
+ ctx, err := NewContext(mockBlockForLib(2000000), tx, contract, context)
+
+ // deploy and init.
+ engine := NewV8Engine(ctx)
+ engine.SetExecutionLimits(100000, 10000000)
+ _, err = engine.DeployAndInit(string(data), tt.sourceType, tt.initArgs)
+ assert.Nil(t, err)
+ engine.Dispose()
+
+ // call.
+ for _, fields := range tt.calls {
+ state, _ := ctx.state.GetOrCreateUserAccount([]byte(account1))
+ fmt.Println("===", state)
+ engine = NewV8Engine(ctx)
+ engine.SetExecutionLimits(100000, 10000000)
+ result, err := engine.Call(string(data), tt.sourceType, fields.function, fields.args)
+ assert.Equal(t, fields.result, result)
+ assert.Nil(t, err)
+ engine.Dispose()
+ }
+ })
+ }
+}
+
+func TestContractsFeatureGetBlockHashAndSeed(t *testing.T) {
+ type fields struct {
+ function string
+ args string
+ result string
+ err error
+ }
+ tests := []struct {
+ contract string
+ sourceType string
+ initArgs string
+ calls []fields
+ }{
+ {
+ "./test/test_contract_features.js",
+ "js",
+ "[]",
+ []fields{
+ {"testGetPreBlockHash1", "[1]", "\"" + byteutils.Hex([]byte("blockHash")) + "\"", nil},
+ {"testGetPreBlockHash1", "[0]", "getPreBlockHash: invalid offset", core.ErrExecutionFailed},
+ {"testGetPreBlockHashByNativeBlock", "[1.1]", "Blockchain.GetPreBlockHash(), argument must be integer", core.ErrExecutionFailed},
+ {"testGetPreBlockSeedByNativeBlock", "[1.1]", "Blockchain.GetPreBlockSeed(), argument must be integer", core.ErrExecutionFailed},
+ {"testGetPreBlockHash1", "[1111111111111111111]", "getPreBlockHash: block not exist", core.ErrExecutionFailed},
+ {"testGetPreBlockSeed1", "[1]", "\"" + byteutils.Hex([]byte("randomSeed")) + "\"", nil},
+ },
+ },
+ }
+
+ account1 := "n1FkntVUMPAsESuCAAPK711omQk19JotBjM"
+ account2 := "n1JNHZJEUvfBYfjDRD14Q73FX62nJAzXkMR"
+
+ for _, tt := range tests {
+ t.Run(tt.contract, func(t *testing.T) {
+ data, err := ioutil.ReadFile(tt.contract)
+ assert.Nil(t, err, "contract path read error")
+
+ mem, _ := storage.NewMemoryStorage()
+ curBlock := mockBlockForLib(2000000)
+
+ preBlock := &corepb.Block{
+ Header: &corepb.BlockHeader{
+ Random: &corepb.Random{
+ VrfSeed: []byte("randomSeed"),
+ },
+ },
+ }
+ preBlockHash := []byte("blockHash")
+ preBlockHeight := curBlock.Height() - 1
+ blockBytes, err := proto.Marshal(preBlock)
+ assert.Nil(t, err)
+
+ mem.Put(byteutils.FromUint64(preBlockHeight), preBlockHash)
+ mem.Put(preBlockHash, blockBytes)
+
+ context, _ := state.NewWorldState(dpos.NewDpos(), mem)
+ add1, _ := core.AddressParse(account1)
+ owner, err := context.GetOrCreateUserAccount(add1.Bytes())
+ assert.Nil(t, err)
+ owner.AddBalance(newUint128FromIntWrapper(1000000000000))
+ add2, _ := core.AddressParse(account2)
+ contract, err := context.CreateContractAccount(add2.Bytes(), nil, &corepb.ContractMeta{Version: "1.0.5"})
+ assert.Nil(t, err)
+ tx := mockNormalTransaction("n1FkntVUMPAsESuCAAPK711omQk19JotBjM", "n1JNHZJEUvfBYfjDRD14Q73FX62nJAzXkMR", "0")
+ ctx, err := NewContext(mockBlockForLib(2000000), tx, contract, context)
+
+ // deploy and init.
+ engine := NewV8Engine(ctx)
+ engine.SetExecutionLimits(100000, 10000000)
+ _, err = engine.DeployAndInit(string(data), tt.sourceType, tt.initArgs)
+ assert.Nil(t, err)
+ engine.Dispose()
+
+ // call.
+ for _, fields := range tt.calls {
+ engine = NewV8Engine(ctx)
+ engine.SetExecutionLimits(100000, 10000000)
+ result, err := engine.Call(string(data), tt.sourceType, fields.function, fields.args)
+ fmt.Println("result", result)
+ assert.Equal(t, fields.result, result)
+ assert.Equal(t, fields.err, err)
+ engine.Dispose()
+ }
+ })
+ }
+}
+
func TestFunctionNameCheck(t *testing.T) {
tests := []struct {
function string
@@ -568,7 +725,7 @@ func TestFunctionNameCheck(t *testing.T) {
owner, err := context.GetOrCreateUserAccount([]byte("account1"))
assert.Nil(t, err)
owner.AddBalance(newUint128FromIntWrapper(1000000))
- contract, _ := context.CreateContractAccount([]byte("account2"), nil)
+ contract, _ := context.CreateContractAccount([]byte("account2"), nil, nil)
ctx, err := NewContext(mockBlock(), mockTransaction(), contract, context)
engine := NewV8Engine(ctx)
@@ -585,7 +742,7 @@ func TestMultiEngine(t *testing.T) {
owner, err := context.GetOrCreateUserAccount([]byte("account1"))
assert.Nil(t, err)
owner.AddBalance(newUint128FromIntWrapper(1000000))
- contract, _ := context.CreateContractAccount([]byte("account2"), nil)
+ contract, _ := context.CreateContractAccount([]byte("account2"), nil, nil)
var wg sync.WaitGroup
for i := 0; i < 100; i++ {
@@ -653,7 +810,7 @@ func TestInstructionCounterTestSuite(t *testing.T) {
owner.AddBalance(newUint128FromIntWrapper(1000000000))
addr, err := core.NewContractAddressFromData([]byte("n1FkntVUMPAsESuCAAPK711omQk19JotBjM"), byteutils.FromUint64(1))
assert.Nil(t, err)
- contract, err := context.CreateContractAccount(addr.Bytes(), nil)
+ contract, err := context.CreateContractAccount(addr.Bytes(), nil, nil)
assert.Nil(t, err)
ctx, err := NewContext(mockBlock(), mockTransaction(), contract, context)
@@ -694,7 +851,7 @@ func TestTypeScriptExecution(t *testing.T) {
owner, err := context.GetOrCreateUserAccount([]byte("account1"))
assert.Nil(t, err)
owner.AddBalance(newUint128FromIntWrapper(1000000000))
- contract, err := context.CreateContractAccount([]byte("account2"), nil)
+ contract, err := context.CreateContractAccount([]byte("account2"), nil, nil)
assert.Nil(t, err)
ctx, err := NewContext(mockBlock(), mockTransaction(), contract, context)
@@ -729,7 +886,7 @@ func DeprecatedTestRunMozillaJSTestSuite(t *testing.T) {
assert.Nil(t, err)
owner.AddBalance(newUint128FromIntWrapper(1000000000))
- contract, err := context.CreateContractAccount([]byte("account2"), nil)
+ contract, err := context.CreateContractAccount([]byte("account2"), nil, nil)
assert.Nil(t, err)
ctx, err := NewContext(mockBlock(), mockTransaction(), contract, context)
@@ -805,10 +962,12 @@ func TestBlockChain(t *testing.T) {
mem, _ := storage.NewMemoryStorage()
context, _ := state.NewWorldState(dpos.NewDpos(), mem)
- owner, err := context.GetOrCreateUserAccount([]byte("n1FkntVUMPAsESuCAAPK711omQk19JotBjM"))
+ addr, _ := core.AddressParse("n1FkntVUMPAsESuCAAPK711omQk19JotBjM")
+ owner, err := context.GetOrCreateUserAccount(addr.Bytes())
assert.Nil(t, err)
owner.AddBalance(newUint128FromIntWrapper(1000000000))
- contract, err := context.CreateContractAccount([]byte("n1JNHZJEUvfBYfjDRD14Q73FX62nJAzXkMR"), nil)
+ addr, _ = core.AddressParse("n1JNHZJEUvfBYfjDRD14Q73FX62nJAzXkMR")
+ contract, err := context.CreateContractAccount(addr.Bytes(), nil, nil)
assert.Nil(t, err)
ctx, err := NewContext(mockBlock(), mockTransaction(), contract, context)
@@ -869,7 +1028,7 @@ func TestBankVaultContract(t *testing.T) {
// prepare the contract.
addr, err := core.NewContractAddressFromData([]byte("n1FkntVUMPAsESuCAAPK711omQk19JotBjM"), byteutils.FromUint64(1))
assert.Nil(t, err)
- contract, _ := context.CreateContractAccount(addr.Bytes(), nil)
+ contract, _ := context.CreateContractAccount(addr.Bytes(), nil, nil)
contract.AddBalance(newUint128FromIntWrapper(5))
// parepare env, block & transactions.
@@ -947,7 +1106,7 @@ func TestEvent(t *testing.T) {
owner, err := context.GetOrCreateUserAccount([]byte("n1FkntVUMPAsESuCAAPK711omQk19JotBjM"))
assert.Nil(t, err)
owner.AddBalance(newUint128FromIntWrapper(1000000000))
- contract, _ := context.CreateContractAccount([]byte("n1JNHZJEUvfBYfjDRD14Q73FX62nJAzXkMR"), nil)
+ contract, _ := context.CreateContractAccount([]byte("n1JNHZJEUvfBYfjDRD14Q73FX62nJAzXkMR"), nil, nil)
ctx, err := NewContext(mockBlock(), mockTransaction(), contract, context)
engine := NewV8Engine(ctx)
@@ -998,7 +1157,7 @@ func TestNRC20Contract(t *testing.T) {
owner.AddBalance(newUint128FromIntWrapper(10000000))
// prepare the contract.
- contract, _ := context.CreateContractAccount([]byte("account2"), nil)
+ contract, _ := context.CreateContractAccount([]byte("account2"), nil, nil)
contract.AddBalance(newUint128FromIntWrapper(5))
// parepare env, block & transactions.
@@ -1152,7 +1311,7 @@ func TestNRC721Contract(t *testing.T) {
assert.Nil(t, err)
// prepare the contract.
- contract, _ := context.CreateContractAccount([]byte("account2"), nil)
+ contract, _ := context.CreateContractAccount([]byte("account2"), nil, nil)
contract.AddBalance(newUint128FromIntWrapper(5))
// parepare env, block & transactions.
@@ -1249,7 +1408,7 @@ func TestNebulasContract(t *testing.T) {
owner.AddBalance(newUint128FromIntWrapper(1000000000))
addr, _ = core.NewContractAddressFromData([]byte{1, 2, 3, 5, 7}, []byte{1, 2, 3, 5, 7})
- contract, _ := context.CreateContractAccount(addr.Bytes(), nil)
+ contract, _ := context.CreateContractAccount(addr.Bytes(), nil, nil)
ctx, err := NewContext(mockBlock(), mockTransaction(), contract, context)
@@ -1316,7 +1475,7 @@ func TestTransferValueFromContracts(t *testing.T) {
owner.AddBalance(newUint128FromIntWrapper(10000000))
addr, err := core.NewContractAddressFromData([]byte("n1FkntVUMPAsESuCAAPK711omQk19JotBjM"), byteutils.FromUint64(1))
assert.Nil(t, err)
- contract, err := context.CreateContractAccount(addr.Bytes(), nil)
+ contract, err := context.CreateContractAccount(addr.Bytes(), nil, nil)
assert.Nil(t, err)
contract.AddBalance(newUint128FromIntWrapper(100))
@@ -1367,7 +1526,7 @@ func TestRequireModule(t *testing.T) {
owner, err := context.GetOrCreateUserAccount([]byte("account1"))
assert.Nil(t, err)
owner.AddBalance(newUint128FromIntWrapper(10000000))
- contract, _ := context.CreateContractAccount([]byte("account2"), nil)
+ contract, _ := context.CreateContractAccount([]byte("account2"), nil, nil)
ctx, err := NewContext(mockBlock(), mockTransaction(), contract, context)
engine := NewV8Engine(ctx)
@@ -1765,10 +1924,12 @@ func TestThreadStackOverflow(t *testing.T) {
mem, _ := storage.NewMemoryStorage()
context, _ := state.NewWorldState(dpos.NewDpos(), mem)
- owner, err := context.GetOrCreateUserAccount([]byte("n1FkntVUMPAsESuCAAPK711omQk19JotBjM"))
+ addr, _ := core.AddressParse("n1FkntVUMPAsESuCAAPK711omQk19JotBjM")
+ owner, err := context.GetOrCreateUserAccount(addr.Bytes())
assert.Nil(t, err)
owner.AddBalance(newUint128FromIntWrapper(1000000000))
- contract, err := context.CreateContractAccount([]byte("n1JNHZJEUvfBYfjDRD14Q73FX62nJAzXkMR"), nil)
+ addr, _ = core.AddressParse("n1JNHZJEUvfBYfjDRD14Q73FX62nJAzXkMR")
+ contract, err := context.CreateContractAccount(addr.Bytes(), nil, nil)
assert.Nil(t, err)
ctx, err := NewContext(mockBlock(), mockTransaction(), contract, context)
@@ -1789,3 +1950,40 @@ func TestThreadStackOverflow(t *testing.T) {
})
}
}
+
+func TestMultiLibVersion(t *testing.T) {
+ tests := []struct {
+ filepath string
+ expectedErr error
+ expectedResult string
+ }{
+ {"test/test_multi_lib_version_require.js", nil, "\"\""},
+ {"test/test_uint.js", nil, "\"\""},
+ {"test/test_date_1.0.5.js", nil, "\"\""},
+ {"test/test_crypto.js", nil, "\"\""},
+ {"test/test_blockchain_1.0.5.js", nil, "\"\""},
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.filepath, func(t *testing.T) {
+ data, err := ioutil.ReadFile(tt.filepath)
+ assert.Nil(t, err, "filepath read error")
+ mem, _ := storage.NewMemoryStorage()
+ context, _ := state.NewWorldState(dpos.NewDpos(), mem)
+ addr, _ := core.AddressParse("n1FF1nz6tarkDVwWQkMnnwFPuPKUaQTdptE")
+ owner, err := context.GetOrCreateUserAccount(addr.Bytes())
+ assert.Nil(t, err)
+ owner.AddBalance(newUint128FromIntWrapper(1000000000000))
+ addr, _ = core.AddressParse("n1p8cwrrfrbFe71eda1PQ6y4WnX3gp8bYze")
+ contract, _ := context.CreateContractAccount(addr.Bytes(), nil, &corepb.ContractMeta{Version: "1.0.5"})
+ ctx, err := NewContext(mockBlockForLib(2000000), mockTransaction(), contract, context)
+
+ engine := NewV8Engine(ctx)
+ engine.SetExecutionLimits(10000000, 10000000)
+ result, err := engine.RunScriptSource(string(data), 0)
+ assert.Equal(t, tt.expectedErr, err)
+ assert.Equal(t, tt.expectedResult, result)
+ engine.Dispose()
+ })
+ }
+}
diff --git a/nf/nvm/event.go b/nf/nvm/event.go
index 3f3bac940..d716af7d4 100644
--- a/nf/nvm/event.go
+++ b/nf/nvm/event.go
@@ -32,6 +32,14 @@ const (
EventBaseGasCount = 20
)
+const (
+ // InnerTransferFailed failed status for transaction execute result.
+ InnerTransferFailed = 0
+
+ // InnerTransferExecutionSuccess success status for transaction execute result.
+ InnerTransferExecutionSuccess = 1
+)
+
// TransferFromContractEvent event for transfer in contract
type TransferFromContractEvent struct {
Amount string `json:"amount"`
@@ -39,6 +47,15 @@ type TransferFromContractEvent struct {
To string `json:"to"`
}
+// TransferFromContractFailureEvent event for transfer in contract
+type TransferFromContractFailureEvent struct {
+ Amount string `json:"amount"`
+ From string `json:"from"`
+ To string `json:"to"`
+ Status uint8 `json:"status"`
+ Error string `json:"error"`
+}
+
// EventTriggerFunc export EventTriggerFunc
//export EventTriggerFunc
func EventTriggerFunc(handler unsafe.Pointer, topic, data *C.char, gasCnt *C.size_t) {
diff --git a/nf/nvm/lib/1.0.0 b/nf/nvm/lib/1.0.0
new file mode 120000
index 000000000..17e50da32
--- /dev/null
+++ b/nf/nvm/lib/1.0.0
@@ -0,0 +1 @@
+../v8/lib/1.0.0
\ No newline at end of file
diff --git a/nf/nvm/lib/1.0.5 b/nf/nvm/lib/1.0.5
new file mode 120000
index 000000000..c1a04a153
--- /dev/null
+++ b/nf/nvm/lib/1.0.5
@@ -0,0 +1 @@
+../v8/lib/1.0.5
\ No newline at end of file
diff --git a/nf/nvm/lib/assert.js b/nf/nvm/lib/assert.js
deleted file mode 120000
index ddafcddbb..000000000
--- a/nf/nvm/lib/assert.js
+++ /dev/null
@@ -1 +0,0 @@
-../v8/lib/assert.js
\ No newline at end of file
diff --git a/nf/nvm/lib/bignumber.js b/nf/nvm/lib/bignumber.js
deleted file mode 120000
index eba9ebef5..000000000
--- a/nf/nvm/lib/bignumber.js
+++ /dev/null
@@ -1 +0,0 @@
-../v8/lib/bignumber.js
\ No newline at end of file
diff --git a/nf/nvm/lib/blockchain.js b/nf/nvm/lib/blockchain.js
deleted file mode 120000
index 67b4d0d49..000000000
--- a/nf/nvm/lib/blockchain.js
+++ /dev/null
@@ -1 +0,0 @@
-../v8/lib/blockchain.js
\ No newline at end of file
diff --git a/nf/nvm/lib/console.js b/nf/nvm/lib/console.js
deleted file mode 120000
index 10c76f55a..000000000
--- a/nf/nvm/lib/console.js
+++ /dev/null
@@ -1 +0,0 @@
-../v8/lib/console.js
\ No newline at end of file
diff --git a/nf/nvm/lib/date.js b/nf/nvm/lib/date.js
deleted file mode 120000
index b2d62a9aa..000000000
--- a/nf/nvm/lib/date.js
+++ /dev/null
@@ -1 +0,0 @@
-../v8/lib/date.js
\ No newline at end of file
diff --git a/nf/nvm/lib/esprima.js b/nf/nvm/lib/esprima.js
deleted file mode 120000
index 8cd1c783b..000000000
--- a/nf/nvm/lib/esprima.js
+++ /dev/null
@@ -1 +0,0 @@
-../v8/lib/esprima.js
\ No newline at end of file
diff --git a/nf/nvm/lib/event.js b/nf/nvm/lib/event.js
deleted file mode 120000
index f0fbddf55..000000000
--- a/nf/nvm/lib/event.js
+++ /dev/null
@@ -1 +0,0 @@
-../v8/lib/event.js
\ No newline at end of file
diff --git a/nf/nvm/lib/execution_env.js b/nf/nvm/lib/execution_env.js
deleted file mode 120000
index 6ec3b8ba2..000000000
--- a/nf/nvm/lib/execution_env.js
+++ /dev/null
@@ -1 +0,0 @@
-../v8/lib/execution_env.js
\ No newline at end of file
diff --git a/nf/nvm/lib/instruction_counter.js b/nf/nvm/lib/instruction_counter.js
deleted file mode 120000
index 5b3f86ee4..000000000
--- a/nf/nvm/lib/instruction_counter.js
+++ /dev/null
@@ -1 +0,0 @@
-../v8/lib/instruction_counter.js
\ No newline at end of file
diff --git a/nf/nvm/lib/random.js b/nf/nvm/lib/random.js
deleted file mode 120000
index 6d184e127..000000000
--- a/nf/nvm/lib/random.js
+++ /dev/null
@@ -1 +0,0 @@
-../v8/lib/random.js
\ No newline at end of file
diff --git a/nf/nvm/lib/storage.js b/nf/nvm/lib/storage.js
deleted file mode 120000
index b15a085c4..000000000
--- a/nf/nvm/lib/storage.js
+++ /dev/null
@@ -1 +0,0 @@
-../v8/lib/storage.js
\ No newline at end of file
diff --git a/nf/nvm/lib/tsc.js b/nf/nvm/lib/tsc.js
deleted file mode 120000
index 8592113a7..000000000
--- a/nf/nvm/lib/tsc.js
+++ /dev/null
@@ -1 +0,0 @@
-../v8/lib/tsc.js
\ No newline at end of file
diff --git a/nf/nvm/lib/typescriptServices.js b/nf/nvm/lib/typescriptServices.js
deleted file mode 120000
index 0ec58b48a..000000000
--- a/nf/nvm/lib/typescriptServices.js
+++ /dev/null
@@ -1 +0,0 @@
-../v8/lib/typescriptServices.js
\ No newline at end of file
diff --git a/nf/nvm/lib/util.js b/nf/nvm/lib/util.js
deleted file mode 120000
index 0088e6865..000000000
--- a/nf/nvm/lib/util.js
+++ /dev/null
@@ -1 +0,0 @@
-../v8/lib/util.js
\ No newline at end of file
diff --git a/nf/nvm/module.go b/nf/nvm/module.go
index c9f20034b..2f3967e9f 100644
--- a/nf/nvm/module.go
+++ b/nf/nvm/module.go
@@ -26,10 +26,17 @@ import (
"strings"
"unsafe"
+ "github.com/nebulasio/go-nebulas/core"
"github.com/nebulasio/go-nebulas/util/logging"
"github.com/sirupsen/logrus"
)
+// const
+const (
+ JSLibRootName = "lib/"
+ JSLibRootNameLen = 4
+)
+
var (
pathRe = regexp.MustCompile("^\\.{0,2}/")
)
@@ -96,6 +103,93 @@ func RequireDelegateFunc(handler unsafe.Pointer, filename *C.char, lineOffset *C
return cSource
}
+// AttachLibVersionDelegateFunc delegate func for lib version choose
+//export AttachLibVersionDelegateFunc
+func AttachLibVersionDelegateFunc(handler unsafe.Pointer, require *C.char) *C.char {
+ libname := C.GoString(require)
+ e := getEngineByEngineHandler(handler)
+ if e == nil {
+ logging.VLog().WithFields(logrus.Fields{
+ "libname": libname,
+ }).Error("delegate handler does not found.")
+ return nil
+ }
+ if len(libname) == 0 {
+ logging.VLog().Error("libname is empty.")
+ return nil
+ }
+
+ if e.ctx == nil {
+ logging.VLog().WithFields(logrus.Fields{
+ "libname": libname,
+ }).Error("e.context is nil.")
+ return nil
+ }
+ if e.ctx.block == nil {
+ logging.VLog().WithFields(logrus.Fields{
+ "libname": libname,
+ }).Error("e.context.block is nil.")
+ return nil
+ }
+
+ // block after core.V8JSLibVersionControlHeight, inclusive
+ if e.ctx.block.Height() >= core.V8JSLibVersionControlHeight {
+ if e.ctx.contract == nil {
+ logging.VLog().WithFields(logrus.Fields{
+ "libname": libname,
+ "height": e.ctx.block.Height(),
+ }).Error("e.context.contract is nil.")
+ return nil
+ }
+ if e.ctx.contract.ContractMeta() == nil {
+ logging.VLog().WithFields(logrus.Fields{
+ "libname": libname,
+ "height": e.ctx.block.Height(),
+ }).Error("e.context.contract.ContractMeta is nil.")
+ return nil
+ }
+ cv := e.ctx.contract.ContractMeta().Version
+
+ if len(cv) == 0 {
+ logging.VLog().WithFields(logrus.Fields{
+ "libname": libname,
+ "height": e.ctx.block.Height(),
+ }).Error("contract deploy lib version is empty.")
+ return nil
+ }
+
+ if !strings.HasPrefix(libname, JSLibRootName) || strings.Contains(libname, "../") {
+ logging.VLog().WithFields(logrus.Fields{
+ "libname": libname,
+ "height": e.ctx.block.Height(),
+ "deployVersion": cv,
+ }).Error("invalid require path.")
+ return nil
+ }
+
+ ver := core.FindLastNearestLibVersion(cv, libname[JSLibRootNameLen:])
+ if len(ver) == 0 {
+ logging.VLog().WithFields(logrus.Fields{
+ "libname": libname,
+ "deployLibVer": cv,
+ }).Error("lib version not found.")
+ return nil
+ }
+
+ return C.CString(JSLibRootName + ver + libname[JSLibRootNameLen-1:])
+ }
+
+ // block created before core.V8JSLibVersionControlHeight, default lib version: 1.0.0
+ if !strings.HasPrefix(libname, JSLibRootName) {
+ if strings.HasPrefix(libname, "/") {
+ libname = "lib" + libname
+ } else {
+ libname = JSLibRootName + libname
+ }
+ }
+ return C.CString(JSLibRootName + core.DefaultV8JSLibVersion + libname[JSLibRootNameLen-1:])
+}
+
func reformatModuleID(id string) string {
paths := make([]string, 0)
for _, p := range strings.Split(id, "/") {
diff --git a/nf/nvm/native-lib/libnebulasv8.so b/nf/nvm/native-lib/libnebulasv8.so
index 00bb8825a..2885283e7 100755
Binary files a/nf/nvm/native-lib/libnebulasv8.so and b/nf/nvm/native-lib/libnebulasv8.so differ
diff --git a/nf/nvm/test/NRC721BasicToken.js b/nf/nvm/test/NRC721BasicToken.js
index 8c02b80da..501eff891 100644
--- a/nf/nvm/test/NRC721BasicToken.js
+++ b/nf/nvm/test/NRC721BasicToken.js
@@ -176,13 +176,13 @@ StandardToken.prototype = {
if (tokenCount.lt(1)) {
throw new Error("Insufficient account balance in removeTokenFrom.");
}
- this.ownedTokensCount.set(_from, tokenCount-1);
+ this.ownedTokensCount.set(_from, tokenCount.sub(1));
},
addTokenTo: function(_to, _tokenId) {
this.tokenOwner.set(_tokenId, _to);
var tokenCount = this.ownedTokensCount.get(_to) || new BigNumber(0);
- this.ownedTokensCount.set(_to, tokenCount+1);
+ this.ownedTokensCount.set(_to, tokenCount.add(1));
},
mint: function(_to, _tokenId) {
diff --git a/nf/nvm/test/contract_crypto.js b/nf/nvm/test/contract_crypto.js
new file mode 100644
index 000000000..d9e04cca8
--- /dev/null
+++ b/nf/nvm/test/contract_crypto.js
@@ -0,0 +1,40 @@
+// since version 1.0.5
+
+'use strict';
+
+var crypto = require('crypto.js');
+
+
+var Contract = function() {
+
+};
+
+Contract.prototype = {
+ init: function(){},
+
+ testSha256: function(data) {
+ return crypto.sha256(data);
+ },
+
+ testSha3256: function(data) {
+ return crypto.sha3256(data);
+ },
+
+ testRipemd160: function(data) {
+ return crypto.ripemd160(data);
+ },
+
+ testRecoverAddress: function(alg, hash, sign) {
+ return crypto.recoverAddress(alg, hash, sign);
+ },
+
+ testMd5: function(data) {
+ return crypto.md5(data);
+ },
+
+ testBase64: function(data) {
+ return crypto.base64(data);
+ }
+};
+
+module.exports = Contract;
diff --git a/nf/nvm/test/contract_date_and_random.js b/nf/nvm/test/contract_date_and_random.js
index 1e2a7c45b..40c86cb65 100644
--- a/nf/nvm/test/contract_date_and_random.js
+++ b/nf/nvm/test/contract_date_and_random.js
@@ -54,7 +54,10 @@ Contract.prototype = {
var date = new Date();
Event.Trigger("Date.TZ", {
- timezone: date.getTimezoneOffset()
+ "timezone": date.getTimezoneOffset(),
+ "toLocaleString": date.toLocaleString(),
+ "toLocaleDateString": date.toLocaleDateString(),
+ "toLocaleTimeString": date.toLocaleTimeString()
});
},
diff --git a/nf/nvm/test/contract_date_and_uint.js b/nf/nvm/test/contract_date_and_uint.js
new file mode 100644
index 000000000..3011a3512
--- /dev/null
+++ b/nf/nvm/test/contract_date_and_uint.js
@@ -0,0 +1,128 @@
+// since version 1.0.5
+
+"use strict";
+
+var Uint64 = Uint.Uint64;
+var Uint128 = Uint.Uint128;
+var Uint256 = Uint.Uint256;
+var Uint512 = Uint.Uint512;
+
+var Contract = function() {
+
+};
+
+Contract.prototype = {
+ init: function(){},
+
+ testDate: function() {
+
+ Event.Trigger("testDate.arguments", {
+ args: arguments
+ });
+
+ var date = arguments.length == 0 ? new Date() : new Date(arguments[0]);
+ var date2 = new Date();
+ date2.setFullYear(1988);
+ var data = {
+ UTC: Date.UTC(),
+ now: Date.now(),
+ parse: Date.parse('04 Dec 1995 00:12:00 GMT'),
+ getUTCDate: date.getUTCDate(),
+ toJSON: date.toJSON(),
+ setFullYear: date2.toString(),
+ height: Blockchain.block.height,
+ timestamp: Blockchain.block.timestamp,
+ valueOf: date.valueOf(),
+ date_toString: date.toString(),
+ date_getTime: date.getTime(),
+ date_getFullYear: date.getFullYear(),
+ equalBlockTime: Blockchain.block.timestamp == (date.getTime() / 1000),
+ toLocaleString: date.toLocaleString(),
+ toLocaleDateString: date.toLocaleDateString(),
+ toLocaleTimeString: date.toLocaleTimeString(),
+ getTimezoneOffset: date.getTimezoneOffset()
+ };
+
+ Event.Trigger("Date", {
+ data: data
+ });
+
+ return data;
+ },
+
+ testUint64: function() {
+ var n1 = new Uint64("10000");
+ var n2 = new Uint64("2000000");
+ var n3 = new Uint512("2000000");
+
+ var overflow = false;
+ try {
+ n2.pow(n1);
+ } catch (err) {
+ if (err.message === "[Uint64 Error] overflow") {
+ overflow = true;
+ }
+ }
+ var underflow = false;
+ try {
+ n1.minus(n2);
+ } catch (err) {
+ if (err.message === "[Uint64 Error] underflow") {
+ underflow = true;
+ }
+ }
+
+ var isNaN = false;
+ try {
+ n1.div(new Uint64(0));
+ } catch (err) {
+ if (err.message === "[Uint64 Error] not an integer") {
+ isNaN = true;
+ }
+ }
+
+ var isNaN2 = false;
+ try {
+ new Uint64(1.2);
+ } catch (err) {
+ if (err.message === "[Uint64 Error] not an integer") {
+ isNaN2 = true;
+ }
+ }
+
+ var incompatible = false;
+ try {
+ n1.plus(n3);
+ } catch (err) {
+ if (err.message === "[Uint64 Error] incompatible type") {
+ incompatible = true;
+ }
+ }
+
+ var data = {
+ "n1": n1.toString(10),
+ "n2": n2.toString(10),
+ "n2pown1Overflow": overflow,
+ "n1minusn2Underflow": underflow,
+ "plus": n1.plus(n2).toString(10),
+ "minus": n2.minus(n1).toString(10),
+ "mul": n1.mul(n2).toString(10),
+ "div": n2.div(n1).toString(10),
+ "mod": n2.mod(n1).toString(10),
+ "pow": n1.pow(new Uint64(2)).toString(10),
+ "n2gtn1": n2.cmp(n1) > 0,
+ "n1isZero": n1.isZero(),
+ "0isZero": new Uint64(0).isZero(),
+ "n1div0NaN": isNaN,
+ "floatNaN": isNaN2,
+ "incompatible": incompatible
+ };
+
+ Event.Trigger("Uint64", {
+ data: data
+ });
+ return data;
+ }
+};
+
+module.exports = Contract;
\ No newline at end of file
diff --git a/nf/nvm/test/incentive_vote_contract.js b/nf/nvm/test/incentive_vote_contract.js
index 5760a314a..ed1ea6d49 100644
--- a/nf/nvm/test/incentive_vote_contract.js
+++ b/nf/nvm/test/incentive_vote_contract.js
@@ -229,11 +229,11 @@ IncentiveVoteContract.prototype = {
},
getAddrVotesList: function() {
var count = this.voteCount;
- var dappVotes = new Array();
+ var dappVotes = {};
for(var i = 0; i < count; i++) {
var addr = this.voteAddrArray.get(i);
var dappVote = this.voteMap.get(addr);
- dappVotes.push(dappVote);
+ dappVotes[addr] = dappVote;
}
return dappVotes;
},
diff --git a/nf/nvm/test/test_blockchain_1.0.5.js b/nf/nvm/test/test_blockchain_1.0.5.js
new file mode 100644
index 000000000..a68598cd7
--- /dev/null
+++ b/nf/nvm/test/test_blockchain_1.0.5.js
@@ -0,0 +1,51 @@
+// Copyright (C) 2018 go-nebulas authors
+//
+// This file is part of the go-nebulas library.
+//
+// the go-nebulas library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU 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-nebulas 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with the go-nebulas library. If not, see .
+//
+
+'use strict';
+
+if (typeof _native_blockchain === "undefined") {
+ throw new Error("_native_blockchain is undefined.");
+}
+
+var ok = Blockchain.transfer("n1FF1nz6tarkDVwWQkMnnwFPuPKUaQTdptE", "1");
+console.log("transfer:" + ok)
+
+var result = Blockchain.verifyAddress("n1FF1nz6tarkDVwWQkMnnwFPuPKUaQTdptE");
+console.log("verifyAddress:" + result)
+
+try {
+ Blockchain.transfer("n1FF1nz6tarkDVwWQkMnnwFPuPKUaQTdptE", -1);
+} catch (err) {
+ if (err.message !== "invalid value" ) {
+ throw err;
+ }
+}
+try {
+ Blockchain.transfer("n1FF1nz6tarkDVwWQkMnnwFPuPKUaQTdptE", NaN);
+} catch (err) {
+ if (err.message !== "invalid value" ) {
+ throw err;
+ }
+}
+try {
+ Blockchain.transfer("n1FF1nz6tarkDVwWQkMnnwFPuPKUaQTdptE", Infinity);
+} catch (err) {
+ if (err.message !== "invalid value" ) {
+ throw err;
+ }
+}
\ No newline at end of file
diff --git a/nf/nvm/test/test_contract_features.js b/nf/nvm/test/test_contract_features.js
new file mode 100644
index 000000000..8867ff472
--- /dev/null
+++ b/nf/nvm/test/test_contract_features.js
@@ -0,0 +1,87 @@
+'use strict';
+
+var testContract = function() {
+
+}
+
+var assert = function(expression, info) {
+ if (!expression) {
+ throw info;
+ }
+};
+
+testContract.prototype = {
+ init: function() {
+
+ },
+
+ testGetAccountState: function() {
+ return Blockchain.getAccountState(Blockchain.transaction.from).balance;
+ },
+
+ testGetAccountStateWrongAddr: function() {
+ return Blockchain.getAccountState("n1JNHZJEUvfBYfjDRD14Q73FX62nJAzXkMR").balance;
+ },
+
+ testGetAccountState1: function() {
+ var from = Blockchain.transaction.from;
+ var to = Blockchain.transaction.to;
+ var value = new BigNumber(Blockchain.transaction.value);
+
+ var fromState1 = Blockchain.getAccountState(from);
+ // console.log("============", JSON.stringify(fromState1));
+ var toState1 = Blockchain.getAccountState(to);
+ // console.log(JSON.stringify(toState1));
+
+ Blockchain.transfer(from, value);
+ var fromState2 = Blockchain.getAccountState(from);
+ // console.log(JSON.stringify(fromState2));
+
+ var toState2 = Blockchain.getAccountState(to);
+ // console.log(JSON.stringify(toState2));
+ var fromBalance1 = new BigNumber(fromState1.balance);
+ var fromBalance2 = new BigNumber(fromState2.balance);
+ console.log(fromBalance1);
+ console.log(fromBalance2);
+ assert(fromBalance1.add(value).equals(fromBalance2), "err 1");
+
+ var toBalance1 = new BigNumber(toState1.balance);
+ var toBalance2 = new BigNumber(toState2.balance);
+ assert(toBalance1.sub(value).equals(toBalance2), "err 2");
+ assert(parseInt(toState1.nonce) == parseInt(toState2.nonce), "err3");
+ },
+
+ testGetAccountState2: function() {
+ return Blockchain.getAccountState("0x1233455");
+ },
+
+ testGetPreBlockHash: function(offset) {
+ var hash = Blockchain.getPreBlockHash(offset);
+ var height = Blockchain.block.height;
+ return {hash: hash, height: height};
+ },
+ testGetPreBlockHash1: function(offset) {
+ return Blockchain.getPreBlockHash(offset);
+ },
+
+ testGetPreBlockSeed: function(offset) {
+ var seed = Blockchain.getPreBlockSeed(offset);
+ var height = Blockchain.block.height;
+ return {seed: seed, height: height};
+ },
+
+ testGetPreBlockSeed1: function(offset) {
+ return Blockchain.getPreBlockSeed(offset);
+ },
+
+ testGetPreBlockHashByNativeBlock: function(offset) {
+ return Blockchain.nativeBlockchain.getPreBlockHash(offset);
+ },
+
+ testGetPreBlockSeedByNativeBlock: function(offset) {
+ return Blockchain.nativeBlockchain.getPreBlockSeed(offset);
+ }
+
+}
+
+module.exports = testContract;
\ No newline at end of file
diff --git a/nf/nvm/test/test_crypto.js b/nf/nvm/test/test_crypto.js
new file mode 100644
index 000000000..1b56fe93a
--- /dev/null
+++ b/nf/nvm/test/test_crypto.js
@@ -0,0 +1,208 @@
+// Copyright (C) 2018 go-nebulas authors
+//
+// This file is part of the go-nebulas library.
+//
+// the go-nebulas library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU 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-nebulas 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with the go-nebulas library. If not, see .
+//
+
+'use strict';
+
+function eq(a, b) {
+ if (a !== b) {
+ throw new Error("Not equal: " + a + " <--> " + b);
+ }
+}
+
+var crypto = require('crypto.js');
+
+var input = "Nebulas is a next generation public blockchain, aiming for a continuously improving ecosystem."
+
+//
+eq(crypto.sha256(input), "a32d6d686968192663b9c9e21e6a3ba1ba9b2e288470c2f98b790256530933e0");
+eq(crypto.sha3256(input), "564733f9f3e139b925cfb1e7e50ba8581e9107b13e4213f2e4708d9c284be75b");
+eq(crypto.ripemd160(input), "4236aa9974eb7b9ddb0f7a7ed06d4bf3d9c0e386");
+eq(crypto.recoverAddress(1, "564733f9f3e139b925cfb1e7e50ba8581e9107b13e4213f2e4708d9c284be75b", "d80e282d165f8c05d8581133df7af3c7c41d51ec7cd8470c18b84a31b9af6a9d1da876ab28a88b0226707744679d4e180691aca6bdef5827622396751a0670c101"), "n1F8QbdnhqpPXDPFT2c9a581tpia8iuF7o2");
+eq(crypto.md5(input), "9954125a33a380c3117269cff93f76a7");
+eq(crypto.base64(input), "TmVidWxhcyBpcyBhIG5leHQgZ2VuZXJhdGlvbiBwdWJsaWMgYmxvY2tjaGFpbiwgYWltaW5nIGZvciBhIGNvbnRpbnVvdXNseSBpbXByb3ZpbmcgZWNvc3lzdGVtLg==");
+
+// alg is not a safe integer
+try {
+ crypto.recoverAddress(1000000000000000000010000000000000000000, "564733f9f3e139b925cfb1e7e50ba8581e9107b13e4213f2e4708d9c284be75b",
+ "d80e282d165f8c05d8581133df7af3c7c41d51ec7cd8470c18b84a31b9af6a9d1da876ab28a88b0226707744679d4e180691aca6bdef5827622396751a0670c101");
+} catch (err) {
+ if (err.message !== "alg must be non-negative integer") {
+ throw err;
+ }
+}
+
+// negative alg
+try {
+ crypto.recoverAddress(-1000, "564733f9f3e139b925cfb1e7e50ba8581e9107b13e4213f2e4708d9c284be75b",
+ "d80e282d165f8c05d8581133df7af3c7c41d51ec7cd8470c18b84a31b9af6a9d1da876ab28a88b0226707744679d4e180691aca6bdef5827622396751a0670c101");
+} catch (err) {
+ if (err.message !== "alg must be non-negative integer") {
+ throw err;
+ }
+}
+
+// odd hash
+try {
+ crypto.recoverAddress(1, "564733f9f3e139b925cfb1e7e50ba8581e9107b13e4213f2e4708d9c284be75",
+ "d80e282d165f8c05d8581133df7af3c7c41d51ec7cd8470c18b84a31b9af6a9d1da876ab28a88b0226707744679d4e180691aca6bdef5827622396751a0670c101");
+} catch (err) {
+ if (err.message !== "hash & sign must be hex string") {
+ throw err;
+ }
+}
+
+// not hex hash
+try {
+ crypto.recoverAddress(1, "TT564733f9f3e139b925cfb1e7e50ba8581e9107b13e4213f2e4708d9c284be75b",
+ "d80e282d165f8c05d8581133df7af3c7c41d51ec7cd8470c18b84a31b9af6a9d1da876ab28a88b0226707744679d4e180691aca6bdef5827622396751a0670c101");
+} catch (err) {
+ if (err.message !== "hash & sign must be hex string") {
+ throw err;
+ }
+}
+
+// not hex sign
+try {
+ crypto.recoverAddress(1, "564733f9f3e139b925cfb1e7e50ba8581e9107b13e4213f2e4708d9c284be75b",
+ "TTd80e282d165f8c05d8581133df7af3c7c41d51ec7cd8470c18b84a31b9af6a9d1da876ab28a88b0226707744679d4e180691aca6bdef5827622396751a0670c101");
+} catch (err) {
+ if (err.message !== "hash & sign must be hex string") {
+ throw err;
+ }
+}
+
+// odd sign
+try {
+ crypto.recoverAddress(1, "564733f9f3e139b925cfb1e7e50ba8581e9107b13e4213f2e4708d9c284be75b",
+ "d80e282d165f8c05d8581133df7af3c7c41d51ec7cd8470c18b84a31b9af6a9d1d876ab28a88b0226707744679d4e180691aca6bdef5827622396751a0670c101");
+} catch (err) {
+ if (err.message !== "hash & sign must be hex string") {
+ throw err;
+ }
+}
+
+try {
+ crypto.nativeCrypto.sha256(1231432);
+} catch (err) {
+ if (err !== "sha256() requires a string argument") {
+ throw err;
+ }
+}
+
+try {
+ crypto.nativeCrypto.sha256();
+} catch (err) {
+ if (err !== "sha256() requires only 1 argument") {
+ throw err;
+ }
+}
+
+try {
+ crypto.nativeCrypto.sha3256(null);
+} catch (err) {
+ if (err !== "sha3256() requires a string argument") {
+ throw err;
+ }
+}
+
+try {
+ crypto.nativeCrypto.sha3256();
+} catch (err) {
+ if (err !== "sha3256() requires only 1 argument") {
+ throw err;
+ }
+}
+
+try {
+ crypto.nativeCrypto.ripemd160();
+} catch (err) {
+ if (err !== "ripemd160() requires only 1 argument") {
+ throw err;
+ }
+}
+
+try {
+ var ret = crypto.nativeCrypto.ripemd(-121);
+} catch (err) {
+ if (err.message !== "crypto.nativeCrypto.ripemd is not a function") {
+ throw err;
+ }
+}
+
+// negative alg
+var ret = crypto.nativeCrypto.recoverAddress(-10, "564733f9f3e139b925cfb1e7e50ba8581e9107b13e4213f2e4708d9c284be75b", "d80e282d165f8c05d8581133df7af3c7c41d51ec7cd8470c18b84a31b9af6a9d1da876ab28a88b0226707744679d4e180691aca6bdef5827622396751a0670c101");
+eq(ret, null);
+
+// invalid alg
+ret = crypto.nativeCrypto.recoverAddress(10, "564733f9f3e139b925cfb1e7e50ba8581e9107b13e4213f2e4708d9c284be75b", "d80e282d165f8c05d8581133df7af3c7c41d51ec7cd8470c18b84a31b9af6a9d1da876ab28a88b0226707744679d4e180691aca6bdef5827622396751a0670c101");
+eq(ret, null);
+
+// odd/invalid sign
+ret = crypto.nativeCrypto.recoverAddress(1, "564733f9f3e139b925cfb1e7e50ba8581e9107b13e4213f2e4708d9c284be75b", "d80e282d165f8c05d8581133df7af3c7c41d51ec7cd");
+eq(ret, null);
+
+// empty sign
+ret = crypto.nativeCrypto.recoverAddress(1, "564733f9f3e139b925cfb1e7e50ba8581e9107b13e4213f2e4708d9c284be75b", "");
+eq(ret, null);
+
+// empty hash
+ret = crypto.nativeCrypto.recoverAddress(1, "", "d80e282d165f8c05d8581133df7af3c7c41d51ec7cd8470c18b84a31b9af6a9d1da876ab28a88b0226707744679d4e180691aca6bdef5827622396751a0670c101");
+eq(ret, null);
+
+// odd/invalid hash
+ret = crypto.nativeCrypto.recoverAddress(1, "564733f9f3e139b925cfb1e7e50ba8581e9107b13e421", "d80e282d165f8c05d8581133df7af3c7c41d51ec7cd8470c18b84a31b9af6a9d1da876ab28a88b0226707744679d4e180691aca6bdef5827622396751a0670c101");
+eq(ret, null);
+
+try {
+ crypto.nativeCrypto.recoverAddress("", "564733f9f3e139b925cfb1e7e50ba8581e9107b13e4213f2e4708d9c284be75b", "d80e282d165f8c05d8581133df7af3c7c41d51ec7cd8470c18b84a31b9af6a9d1da876ab28a88b0226707744679d4e180691aca6bdef5827622396751a0670c101");
+} catch (err) {
+ if (err !== "recoverAddress(): 1st arg should be integer") {
+ throw err;
+ }
+}
+try {
+ crypto.nativeCrypto.recoverAddress(null, "564733f9f3e139b925cfb1e7e50ba8581e9107b13e4213f2e4708d9c284be75b", "d80e282d165f8c05d8581133df7af3c7c41d51ec7cd8470c18b84a31b9af6a9d1da876ab28a88b0226707744679d4e180691aca6bdef5827622396751a0670c101");
+} catch (err) {
+ if (err !== "recoverAddress(): 1st arg should be integer") {
+ throw err;
+ }
+}
+
+try {
+ crypto.nativeCrypto.recoverAddress(1, 123, "d80e282d165f8c05d8581133df7af3c7c41d51ec7cd8470c18b84a31b9af6a9d1da876ab28a88b0226707744679d4e180691aca6bdef5827622396751a0670c101");
+} catch (err) {
+ if (err !== "recoverAddress(): 2nd arg should be string") {
+ throw err;
+ }
+}
+
+try {
+ crypto.nativeCrypto.recoverAddress(1, "564733f9f3e139b925cfb1e7e50ba8581e9107b13e4213f2e4708d9c284be75b", null);
+} catch (err) {
+ if (err !== "recoverAddress(): 3rd arg should be string") {
+ throw err;
+ }
+}
+
+try {
+ crypto.nativeCrypto.recoverAddress(1, "564733f9f3e139b925cfb1e7e50ba8581e9107b13e4213f2e4708d9c284be75b");
+} catch (err) {
+ if (err !== "recoverAddress() requires 3 arguments") {
+ throw err;
+ }
+}
diff --git a/nf/nvm/test/test_date_1.0.5.js b/nf/nvm/test/test_date_1.0.5.js
new file mode 100644
index 000000000..b2c886356
--- /dev/null
+++ b/nf/nvm/test/test_date_1.0.5.js
@@ -0,0 +1,116 @@
+// Copyright (C) 2018 go-nebulas authors
+//
+// This file is part of the go-nebulas library.
+//
+// the go-nebulas library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU 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-nebulas 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with the go-nebulas library. If not, see .
+//
+
+function eq(a, b) {
+ if (a !== b) {
+ throw new Error("Not equal: " + a + " <--> " + b);
+ }
+}
+
+Blockchain.blockParse("{\"timestamp\":20000000000,\"seed\":\"\"}");
+
+var date = new Date();
+var options = { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' };
+
+eq(date.toDateString(), "Tue Oct 11 2603");
+eq(Date.UTC(2603, 9, 11, 11, 33, 20), 20000000000000);
+eq(date.getTimezoneOffset(), 0);
+eq(date.toTimeString(), "11:33:20 GMT+0000 (UTC)");
+eq(date.toString(), "Tue Oct 11 2603 11:33:20 GMT+0000 (UTC)");
+eq(date.toGMTString(), "Tue, 11 Oct 2603 11:33:20 GMT");
+eq(date.toUTCString(), "Tue, 11 Oct 2603 11:33:20 GMT");
+eq(date.toISOString(), "2603-10-11T11:33:20.000Z");
+eq(date.toJSON(), "2603-10-11T11:33:20.000Z");
+eq(date.valueOf(), 20000000000000);
+
+eq(Object.prototype.toLocaleString.call(date), "Tue Oct 11 2603 11:33:20 GMT+0000 (UTC)");
+eq(Object.prototype.toLocaleString.call(date, 'ko-KR', { timeZone: 'UTC' }), "Tue Oct 11 2603 11:33:20 GMT+0000 (UTC)");
+eq(date.toLocaleString(), "10/11/2603, 11:33:20 AM");
+eq(date.toLocaleString('ko-KR', { timeZone: 'UTC' }), "2603. 10. 11. 오전 11:33:20");
+
+eq(date.toLocaleDateString(), "10/11/2603");
+eq(date.toLocaleDateString('de-DE', options), "Dienstag, 11. Oktober 2603");
+
+eq(date.toLocaleTimeString(), "11:33:20 AM");
+eq(date.toLocaleTimeString('ar-EG'), "١١:٣٣:٢٠ ص");
+
+
+eq(date.getDate(), date.getUTCDate());
+eq(date.getDay(), date.getUTCDay());
+eq(date.getFullYear(), date.getUTCFullYear());
+eq(date.getHours(), date.getUTCHours());
+eq(date.getMilliseconds(), date.getUTCMilliseconds());
+eq(date.getMinutes(), date.getUTCMinutes());
+eq(date.getMonth(), date.getUTCMonth());
+eq(date.getSeconds(), date.getUTCSeconds());
+
+try {
+ date.getYear();
+ throw new Error("should not be here.");
+} catch(err) {
+ if (err != "Error: Deprecated!") {
+ throw err;
+ }
+}
+try {
+ date.setYear(1999);
+ throw new Error("should not be here.");
+} catch(err) {
+ if (err != "Error: Deprecated!") {
+ throw err;
+ }
+}
+
+date = new Date('August 19, 2017 23:15:30');
+var date2 = new Date('August 19, 2017 23:15:30');
+
+date.setDate(12);
+date2.setUTCDate(12);
+eq(date - date2, 0);
+
+date.setMonth(1);
+date2.setUTCMonth(1);
+eq(date - date2, 0);
+
+date.setFullYear(1999);
+date2.setUTCFullYear(1999);
+eq(date - date2, 0);
+
+date.setHours(22);
+date2.setUTCHours(22);
+eq(date - date2, 0);
+
+date.setMilliseconds(420);
+date2.setUTCMilliseconds(420);
+eq(date - date2, 0);
+
+date.setMinutes(12);
+date2.setUTCMinutes(12);
+eq(date - date2, 0);
+
+date.setSeconds(12);
+date2.setUTCSeconds(12);
+eq(date - date2, 0);
+
+
+
+var d1 = new Date('December 17, 1995 00:00:00');
+var d2 = new Date(d1.getUTCFullYear(), d1.getUTCMonth(), d1.getUTCDate());
+eq(d1.getTime(), d2.getTime());
+
+eq(Date.parse(Date()), 20000000000000);
\ No newline at end of file
diff --git a/nf/nvm/test/test_multi_lib_version_require.js b/nf/nvm/test/test_multi_lib_version_require.js
new file mode 100644
index 000000000..d32fde265
--- /dev/null
+++ b/nf/nvm/test/test_multi_lib_version_require.js
@@ -0,0 +1,43 @@
+// Copyright (C) 2018 go-nebulas authors
+//
+// This file is part of the go-nebulas library.
+//
+// the go-nebulas library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU 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-nebulas 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with the go-nebulas library. If not, see .
+//
+
+'use strict';
+
+var console2 = require('console.js');
+if (!Object.is(console, console2)) {
+ throw new Error("require should caches libs.");
+}
+
+var err = new Error("require should throw error when file does not exist.");
+try {
+ require("./not-exist-file");
+ throw err;
+} catch (e) {
+ if (e === err) {
+ throw e;
+ }
+}
+
+try {
+ require("/ab/c/console.js");
+ throw err;
+} catch (e) {
+ if (e === err) {
+ throw e;
+ }
+}
diff --git a/nf/nvm/test/test_uint.js b/nf/nvm/test/test_uint.js
new file mode 100644
index 000000000..7961cd252
--- /dev/null
+++ b/nf/nvm/test/test_uint.js
@@ -0,0 +1,152 @@
+// Copyright (C) 2018 go-nebulas authors
+//
+// This file is part of the go-nebulas library.
+//
+// the go-nebulas library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU 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-nebulas 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with the go-nebulas library. If not, see .
+//
+
+'use strict';
+
+var Uint64 = Uint.Uint64;
+var Uint128 = Uint.Uint128;
+var Uint256 = Uint.Uint256;
+var Uint512 = Uint.Uint512;
+
+var err1 = "[Uint64 Error] overflow";
+var err2 = '[Uint128 Error] incompatible type';
+var err3 = "[Uint64 Error] underflow";
+var err4 = '[Uint512 Error] incompatible type';
+var err5 = '[Uint256 Error] overflow';
+var err6 = '[Uint64 Error] NaN';
+var err7 = '[Uint64 Error] not an integer';
+
+var a = new Uint64(100000000000000);
+var a1 = new Uint64(100000000000001);
+var b = new Uint128(100000000000000);
+var c = new Uint256(100000000000000);
+var d = new Uint512(100000000000000);
+
+var f = new Uint64(0);
+if (f.toString(10) !== "0") {
+ throw new Error("not eq");
+}
+
+if (!Uint.isUint(a)) {
+ throw new Error("uint64 should be uint");
+}
+
+if (Uint.isUint(new BigNumber("123"))) {
+ throw new Error("bignumber should not be uint");
+}
+
+if (Uint.isUint(123)) {
+ throw new Error("number should not be uint");
+}
+
+if (Uint.isUint("123")) {
+ throw new Error("string should not be uint");
+}
+
+// overflow
+try {
+ a.pow(new Uint64(2));
+} catch (e) {
+ if (e.message !== err1) {
+ throw e;
+ }
+}
+a.pow(new Uint64(1));
+
+try {
+ c.mul(c).mul(c).mul(c).mul(c).mul(c).mul(c);
+} catch (e) {
+ if (e.message !== err5) {
+ throw e;
+ }
+}
+
+var bpow2 = b.pow(new Uint128(2));
+if (bpow2.toString(10) !== "10000000000000000000000000000") {
+ throw new Error("b.pow(2) not equal");
+}
+
+// incompatible
+try {
+ b.plus(c);
+} catch (e) {
+ if (e.message !== err2) {
+ throw e;
+ }
+}
+b.plus(b);
+
+try {
+ d.minus(1);
+} catch (e) {
+ if (e.message !== err4) {
+ throw e;
+ }
+}
+
+// underflow
+try {
+ a.minus(a1);
+} catch (e) {
+ if (e.message !== err3) {
+ throw e;
+ }
+}
+if (a.minus(a).toString(10) !== "0") {
+ throw new Error("a.minus(a) not 0");
+}
+
+// NaN
+try {
+ a.div(null);
+} catch (e) {
+ if (e.message !== err6) {
+ throw e;
+ }
+}
+
+if (a.div(a).toString(10) !== "1") {
+ throw new Error("a.div(a) not 1");
+}
+
+if (a.mod(a).toString(10) !== "0") {
+ throw new Error("a.mod(a) not 0");
+}
+
+// not an integer
+try {
+ new Uint64(1.2);
+} catch (e) {
+ if (e.message !== err7) {
+ throw e;
+ }
+}
+try {
+ new Uint64("1.2");
+} catch (e) {
+ if (e.message !== err7) {
+ throw e;
+ }
+}
+try {
+ a.div(new Uint64(0));
+} catch (e) {
+ if (e.message !== err7) {
+ throw e;
+ }
+}
\ No newline at end of file
diff --git a/nf/nvm/test/transfer_value_from_contract.js b/nf/nvm/test/transfer_value_from_contract.js
index b0d0ae034..ae7bac332 100644
--- a/nf/nvm/test/transfer_value_from_contract.js
+++ b/nf/nvm/test/transfer_value_from_contract.js
@@ -30,6 +30,9 @@ TransferValueContract.prototype = {
return 0
}
},
+
+ accept: function() {
+ },
}
module.exports = TransferValueContract;
\ No newline at end of file
diff --git a/nf/nvm/types.go b/nf/nvm/types.go
index adc44f260..6417bfade 100644
--- a/nf/nvm/types.go
+++ b/nf/nvm/types.go
@@ -4,6 +4,7 @@ import (
"errors"
"github.com/nebulasio/go-nebulas/core"
+ "github.com/nebulasio/go-nebulas/core/pb"
"github.com/nebulasio/go-nebulas/core/state"
"github.com/nebulasio/go-nebulas/storage"
"github.com/nebulasio/go-nebulas/util"
@@ -52,6 +53,31 @@ const (
TransferAddressFailed
)
+//the max recent block number can query
+const (
+ maxQueryBlockInfoValidTime = 30
+ maxBlockOffset = maxQueryBlockInfoValidTime * 24 * 3600 * 1000 / 15000 //TODO:dpos.BlockIntervalInMs
+)
+
+// define gas consume
+const (
+ // crypto
+ CryptoSha256GasBase = 20000
+ CryptoSha3256GasBase = 20000
+ CryptoRipemd160GasBase = 20000
+ CryptoRecoverAddressGasBase = 100000
+ CryptoMd5GasBase = 6000
+ CryptoBase64GasBase = 3000
+
+ //In blockChain
+ GetTxByHashGasBase = 1000
+ GetAccountStateGasBase = 2000
+ TransferGasBase = 2000
+ VerifyAddressGasBase = 100
+ GetPreBlockHashGasBase = 2000
+ GetPreBlockSeedGasBase = 2000
+)
+
// Block interface breaks cycle import dependency and hides unused services.
type Block interface {
Hash() byteutils.Hash
@@ -84,6 +110,7 @@ type Account interface {
Put(key []byte, value []byte) error
Get(key []byte) ([]byte, error)
Del(key []byte) error
+ ContractMeta() *corepb.ContractMeta
}
// WorldState interface breaks cycle import dependency and hides unused services.
@@ -91,4 +118,6 @@ type WorldState interface {
GetOrCreateUserAccount(addr byteutils.Hash) (state.Account, error)
GetTx(txHash byteutils.Hash) ([]byte, error)
RecordEvent(txHash byteutils.Hash, event *state.Event)
+ GetBlockHashByHeight(height uint64) ([]byte, error)
+ GetBlock(txHash byteutils.Hash) ([]byte, error)
}
diff --git a/nf/nvm/v8/Makefile b/nf/nvm/v8/Makefile
index 8d83f3ef2..095fc2876 100644
--- a/nf/nvm/v8/Makefile
+++ b/nf/nvm/v8/Makefile
@@ -56,10 +56,10 @@ all: main
%.cpp.o: %.cpp
$(CXX) $(CXXFLAGS) -c $< -o $<.o
-main: samples/main.cc.o samples/memory_storage.cc.o samples/memory_modules.cc.o engine.cc.o allocator.cc.o lib/global.cc.o lib/execution_env.cc.o lib/storage_object.cc.o lib/log_callback.cc.o lib/require_callback.cc.o lib/instruction_counter.cc.o lib/blockchain.cc.o lib/fake_blockchain.cc.o lib/tracing.cc.o lib/file.cc.o lib/util.cc.o lib/typescript.cc.o lib/event.cc.o
+main: samples/main.cc.o samples/memory_storage.cc.o samples/memory_modules.cc.o engine.cc.o allocator.cc.o lib/global.cc.o lib/execution_env.cc.o lib/storage_object.cc.o lib/log_callback.cc.o lib/require_callback.cc.o lib/instruction_counter.cc.o lib/blockchain.cc.o lib/fake_blockchain.cc.o lib/tracing.cc.o lib/file.cc.o lib/util.cc.o lib/typescript.cc.o lib/event.cc.o lib/crypto.cc.o
$(LD) $(LDFLAGS) $^ -o $@ $(LIBS_PATH) $(LIBS)
-engine: engine.cc.o thread_engine.cc.o allocator.cc.o lib/global.cc.o lib/execution_env.cc.o lib/storage_object.cc.o lib/log_callback.cc.o lib/require_callback.cc.o lib/instruction_counter.cc.o lib/blockchain.cc.o lib/tracing.cc.o lib/file.cc.o lib/util.cc.o lib/typescript.cc.o lib/event.cc.o
+engine: engine.cc.o thread_engine.cc.o allocator.cc.o lib/global.cc.o lib/execution_env.cc.o lib/storage_object.cc.o lib/log_callback.cc.o lib/require_callback.cc.o lib/instruction_counter.cc.o lib/blockchain.cc.o lib/tracing.cc.o lib/file.cc.o lib/util.cc.o lib/typescript.cc.o lib/event.cc.o lib/crypto.cc.o
$(LD) -shared $(LDFLAGS) $^ -o libnebulasv8$(DYLIB) $(LIBS_PATH) $(LIBS)
install: engine
diff --git a/nf/nvm/v8/engine.cc b/nf/nvm/v8/engine.cc
index c89fa9012..974da8cf4 100644
--- a/nf/nvm/v8/engine.cc
+++ b/nf/nvm/v8/engine.cc
@@ -47,6 +47,7 @@ void PrintAndReturnException(char **exception, Local context,
void EngineLimitsCheckDelegate(Isolate *isolate, size_t count,
void *listenerContext);
+#define ExecuteTimeOut 5*1000*1000
#define STRINGIZE2(s) #s
#define STRINGIZE(s) STRINGIZE2(s)
#define V8VERSION_STRING \
@@ -101,6 +102,7 @@ V8Engine *CreateEngine() {
V8Engine *e = (V8Engine *)calloc(1, sizeof(V8Engine));
e->allocator = allocator;
e->isolate = isolate;
+ e->timeout = ExecuteTimeOut;
return e;
}
@@ -130,14 +132,14 @@ int ExecuteSourceDataDelegate(char **result, Isolate *isolate,
if (script.IsEmpty()) {
PrintAndReturnException(result, context, trycatch);
- return 1;
+ return NVM_EXCEPTION_ERR;
}
// Run the script to get the result.
MaybeLocal ret = script.ToLocalChecked()->Run(context);
if (ret.IsEmpty()) {
PrintAndReturnException(result, context, trycatch);
- return 1;
+ return NVM_EXCEPTION_ERR;
}
// set result.
@@ -153,7 +155,7 @@ int ExecuteSourceDataDelegate(char **result, Isolate *isolate,
}
}
- return 0;
+ return NVM_SUCCESS;
}
char *InjectTracingInstructions(V8Engine *e, const char *source,
@@ -222,11 +224,17 @@ int Execute(char **result, V8Engine *e, const char *source,
// Setup execution env.
if (SetupExecutionEnv(isolate, context)) {
PrintAndReturnException(result, context, trycatch);
- return 1;
+ return NVM_EXCEPTION_ERR;
}
- return delegate(result, isolate, source, source_line_offset, context,
+ int retTmp = delegate(result, isolate, source, source_line_offset, context,
trycatch, delegateContext);
+
+ if (e->is_unexpected_error_happen) {
+ return NVM_UNEXPECTED_ERR;
+ }
+
+ return retTmp;
}
void PrintException(Local context, TryCatch &trycatch) {
@@ -301,7 +309,12 @@ void PrintAndReturnException(char **exception, Local context,
// return exception message.
if (exception != NULL) {
*exception = (char *)malloc(exception_str.length() + 1);
- strcpy(*exception, *exception_str);
+ //TODO: Branch code detected “(v8::String::Utf8Value) $0 = (str_ = , length_ = 0)”
+ if (exception_str.length() == 0) {
+ strcpy(*exception, "");
+ } else {
+ strcpy(*exception, *exception_str);
+ }
}
}
@@ -335,7 +348,7 @@ void TerminateExecution(V8Engine *e) {
}
Isolate *isolate = static_cast(e->isolate);
isolate->TerminateExecution();
- e->is_requested_terminate_execution = 1;
+ e->is_requested_terminate_execution = true;
}
void EngineLimitsCheckDelegate(Isolate *isolate, size_t count,
@@ -355,11 +368,11 @@ int IsEngineLimitsExceeded(V8Engine *e) {
e->limits_of_executed_instructions <
e->stats.count_of_executed_instructions) {
// Reach instruction limits.
- return 1;
+ return NVM_GAS_LIMIT_ERR;
} else if (e->limits_of_total_memory_size > 0 &&
e->limits_of_total_memory_size < e->stats.total_memory_size) {
// reach memory limits.
- return 2;
+ return NVM_MEM_LIMIT_ERR;
}
return 0;
diff --git a/nf/nvm/v8/engine.h b/nf/nvm/v8/engine.h
index fca988a49..9cce02a79 100644
--- a/nf/nvm/v8/engine.h
+++ b/nf/nvm/v8/engine.h
@@ -35,6 +35,7 @@ extern "C" {
#include
#include
#include
+#include "lib/nvm_error.h"
enum LogLevel {
DEBUG = 1,
@@ -72,17 +73,40 @@ EXPORT void InitializeStorage(StorageGetFunc get, StoragePutFunc put,
// blockchain
typedef char *(*GetTxByHashFunc)(void *handler, const char *hash,
size_t *counterVal);
-typedef char *(*GetAccountStateFunc)(void *handler, const char *address,
- size_t *counterVal);
+typedef int (*GetAccountStateFunc)(void *handler, const char *address,
+ size_t *counterVal, char **result, char **info);
typedef int (*TransferFunc)(void *handler, const char *to, const char *value,
size_t *counterVal);
typedef int (*VerifyAddressFunc)(void *handler, const char *address,
size_t *counterVal);
+typedef int (*GetPreBlockHashFunc)(void *handler, unsigned long long offset, size_t *counterVal, char **result, char **info);
+
+typedef int (*GetPreBlockSeedFunc)(void *handler, unsigned long long offset, size_t *counterVal, char **result, char **info);
+
+
EXPORT void InitializeBlockchain(GetTxByHashFunc getTx,
GetAccountStateFunc getAccount,
TransferFunc transfer,
- VerifyAddressFunc verifyAddress);
+ VerifyAddressFunc verifyAddress,
+ GetPreBlockHashFunc getPreBlockHash,
+ GetPreBlockSeedFunc getPreBlockSeed);
+
+// crypto
+typedef char *(*Sha256Func)(const char *data, size_t *counterVal);
+typedef char *(*Sha3256Func)(const char *data, size_t *counterVal);
+typedef char *(*Ripemd160Func)(const char *data, size_t *counterVal);
+typedef char *(*RecoverAddressFunc)(int alg, const char *data, const char *sign,
+ size_t *counterVal);
+typedef char *(*Md5Func)(const char *data, size_t *counterVal);
+typedef char *(*Base64Func)(const char *data, size_t *counterVal);
+
+EXPORT void InitializeCrypto(Sha256Func sha256,
+ Sha3256Func sha3256,
+ Ripemd160Func ripemd160,
+ RecoverAddressFunc recoverAddress,
+ Md5Func md5,
+ Base64Func base64);
// version
EXPORT char *GetV8Version();
@@ -90,7 +114,11 @@ EXPORT char *GetV8Version();
// require callback.
typedef char *(*RequireDelegate)(void *handler, const char *filename,
size_t *lineOffset);
-EXPORT void InitializeRequireDelegate(RequireDelegate delegate);
+typedef char *(*AttachLibVersionDelegate)(void *handler, const char *libname);
+
+EXPORT void InitializeRequireDelegate(RequireDelegate delegate, AttachLibVersionDelegate libDelegate);
+
+EXPORT void InitializeExecutionEnvDelegate(AttachLibVersionDelegate libDelegate);
typedef struct V8EngineStats {
size_t count_of_executed_instructions;
@@ -108,12 +136,15 @@ typedef struct V8EngineStats {
} V8EngineStats;
typedef struct V8Engine {
+
void *isolate;
void *allocator;
size_t limits_of_executed_instructions;
size_t limits_of_total_memory_size;
- int is_requested_terminate_execution;
+ bool is_requested_terminate_execution;
+ bool is_unexpected_error_happen;
int testing;
+ int timeout;
V8EngineStats stats;
@@ -135,7 +166,7 @@ typedef struct v8ThreadContext_ {
V8Engine *e;
v8ThreadContextInput input;
v8ThreadContextOutput output;
- bool is_finished;
+ bool is_finished;
} v8ThreadContext;
EXPORT void Initialize();
diff --git a/nf/nvm/v8/lib/assert.js b/nf/nvm/v8/lib/1.0.0/assert.js
similarity index 100%
rename from nf/nvm/v8/lib/assert.js
rename to nf/nvm/v8/lib/1.0.0/assert.js
diff --git a/nf/nvm/v8/lib/bignumber.js b/nf/nvm/v8/lib/1.0.0/bignumber.js
similarity index 100%
rename from nf/nvm/v8/lib/bignumber.js
rename to nf/nvm/v8/lib/1.0.0/bignumber.js
diff --git a/nf/nvm/v8/lib/blockchain.js b/nf/nvm/v8/lib/1.0.0/blockchain.js
similarity index 99%
rename from nf/nvm/v8/lib/blockchain.js
rename to nf/nvm/v8/lib/1.0.0/blockchain.js
index 529d1be22..b6db4b63e 100644
--- a/nf/nvm/v8/lib/blockchain.js
+++ b/nf/nvm/v8/lib/1.0.0/blockchain.js
@@ -72,6 +72,7 @@ Blockchain.prototype = {
var ret = this.nativeBlockchain.transfer(address, value.toString(10));
return ret == 0;
},
+
verifyAddress: function (address) {
return this.nativeBlockchain.verifyAddress(address);
}
diff --git a/nf/nvm/v8/lib/console.js b/nf/nvm/v8/lib/1.0.0/console.js
similarity index 100%
rename from nf/nvm/v8/lib/console.js
rename to nf/nvm/v8/lib/1.0.0/console.js
diff --git a/nf/nvm/v8/lib/date.js b/nf/nvm/v8/lib/1.0.0/date.js
similarity index 100%
rename from nf/nvm/v8/lib/date.js
rename to nf/nvm/v8/lib/1.0.0/date.js
diff --git a/nf/nvm/v8/lib/esprima.js b/nf/nvm/v8/lib/1.0.0/esprima.js
similarity index 100%
rename from nf/nvm/v8/lib/esprima.js
rename to nf/nvm/v8/lib/1.0.0/esprima.js
diff --git a/nf/nvm/v8/lib/event.js b/nf/nvm/v8/lib/1.0.0/event.js
similarity index 100%
rename from nf/nvm/v8/lib/event.js
rename to nf/nvm/v8/lib/1.0.0/event.js
diff --git a/nf/nvm/v8/lib/execution_env.js b/nf/nvm/v8/lib/1.0.0/execution_env.js
similarity index 100%
rename from nf/nvm/v8/lib/execution_env.js
rename to nf/nvm/v8/lib/1.0.0/execution_env.js
diff --git a/nf/nvm/v8/lib/instruction_counter.js b/nf/nvm/v8/lib/1.0.0/instruction_counter.js
similarity index 100%
rename from nf/nvm/v8/lib/instruction_counter.js
rename to nf/nvm/v8/lib/1.0.0/instruction_counter.js
diff --git a/nf/nvm/v8/lib/random.js b/nf/nvm/v8/lib/1.0.0/random.js
similarity index 100%
rename from nf/nvm/v8/lib/random.js
rename to nf/nvm/v8/lib/1.0.0/random.js
diff --git a/nf/nvm/v8/lib/storage.js b/nf/nvm/v8/lib/1.0.0/storage.js
similarity index 100%
rename from nf/nvm/v8/lib/storage.js
rename to nf/nvm/v8/lib/1.0.0/storage.js
diff --git a/nf/nvm/v8/lib/tsc.js b/nf/nvm/v8/lib/1.0.0/tsc.js
similarity index 100%
rename from nf/nvm/v8/lib/tsc.js
rename to nf/nvm/v8/lib/1.0.0/tsc.js
diff --git a/nf/nvm/v8/lib/typescriptServices.js b/nf/nvm/v8/lib/1.0.0/typescriptServices.js
similarity index 100%
rename from nf/nvm/v8/lib/typescriptServices.js
rename to nf/nvm/v8/lib/1.0.0/typescriptServices.js
diff --git a/nf/nvm/v8/lib/util.js b/nf/nvm/v8/lib/1.0.0/util.js
similarity index 100%
rename from nf/nvm/v8/lib/util.js
rename to nf/nvm/v8/lib/1.0.0/util.js
diff --git a/nf/nvm/v8/lib/1.0.5/blockchain.js b/nf/nvm/v8/lib/1.0.5/blockchain.js
new file mode 100644
index 000000000..370193951
--- /dev/null
+++ b/nf/nvm/v8/lib/1.0.5/blockchain.js
@@ -0,0 +1,133 @@
+// Copyright (C) 2017 go-nebulas authors
+//
+// This file is part of the go-nebulas library.
+//
+// the go-nebulas library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU 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-nebulas 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with the go-nebulas library. If not, see .
+//
+
+'use strict';
+
+var Blockchain = function () {
+ Object.defineProperty(this, "nativeBlockchain", {
+ configurable: false,
+ enumerable: false,
+ get: function(){
+ return _native_blockchain;
+ }
+ });
+};
+
+Blockchain.prototype = {
+ AccountAddress: 0x57,
+ ContractAddress: 0x58,
+
+ blockParse: function (str) {
+ var block = JSON.parse(str);
+ if (block != null) {
+ var fb = Object.freeze(block);
+ Object.defineProperty(this, "block", {
+ configurable: false,
+ enumerable: false,
+ get: function(){
+ return fb;
+ }
+ });
+ }
+ },
+ transactionParse: function (str) {
+ var tx = JSON.parse(str);
+ if (tx != null) {
+ var value = tx.value === undefined || tx.value.length === 0 ? "0" : tx.value;
+ tx.value = new BigNumber(value);
+ var gasPrice = tx.gasPrice === undefined || tx.gasPrice.length === 0 ? "0" : tx.gasPrice;
+ tx.gasPrice = new BigNumber(gasPrice);
+ var gasLimit = tx.gasLimit === undefined || tx.gasLimit.length === 0 ? "0" : tx.gasLimit;
+ tx.gasLimit = new BigNumber(gasLimit);
+
+ var ft = Object.freeze(tx);
+ Object.defineProperty(this, "transaction", {
+ configurable: false,
+ enumerable: false,
+ get: function(){
+ return ft;
+ }
+ });
+ }
+ },
+ transfer: function (address, value) {
+ if (!Uint.isUint(value)) {
+ if (!(value instanceof BigNumber)) {
+ value = new BigNumber(value);
+ }
+ if (value.isNaN() || value.isNegative() || !value.isFinite()) {
+ throw new Error("invalid value");
+ }
+ }
+
+ var ret = this.nativeBlockchain.transfer(address, value.toString(10));
+ return ret == 0;
+ },
+
+ verifyAddress: function (address) {
+ return this.nativeBlockchain.verifyAddress(address);
+ },
+
+ getAccountState: function(address) {
+ if (address) {
+ var result = this.nativeBlockchain.getAccountState(address);
+ if (result) {
+ return JSON.parse(result);
+ } else {
+ throw "getAccountState: invalid address";
+ }
+ } else {
+ throw "getAccountState: inValid address";
+ }
+ },
+
+ getPreBlockHash: function (offset) {
+ offset = parseInt(offset);
+ if (!offset) {
+ throw "getPreBlockHash: invalid offset"
+ }
+
+ if (offset <= 0) {
+ throw "getPreBlockHash: offset should large than 0"
+ }
+
+ if (offset >= this.block.height) {
+ throw "getPreBlockHash: block not exist"
+ }
+
+ return this.nativeBlockchain.getPreBlockHash(offset);
+ },
+
+ getPreBlockSeed: function (offset) {
+ offset = parseInt(offset);
+ if (!offset) {
+ throw "getPreBlockSeed: invalid offset"
+ }
+
+ if (offset <= 0) {
+ throw "getPreBlockSeed: offset should large than 0"
+ }
+
+ if (offset >= this.block.height) {
+ throw "getPreBlockSeed: block not exist"
+ }
+
+ return this.nativeBlockchain.getPreBlockSeed(offset);
+ }
+};
+module.exports = new Blockchain();
diff --git a/nf/nvm/v8/lib/1.0.5/crypto.js b/nf/nvm/v8/lib/1.0.5/crypto.js
new file mode 100644
index 000000000..d91994e88
--- /dev/null
+++ b/nf/nvm/v8/lib/1.0.5/crypto.js
@@ -0,0 +1,97 @@
+// Copyright (C) 2018 go-nebulas authors
+//
+// This file is part of the go-nebulas library.
+//
+// the go-nebulas library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU 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-nebulas 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with the go-nebulas library. If not, see .
+//
+
+'use strict';
+
+const HexStringRegex = /^[0-9a-fA-F]+$/;
+
+var Crypto = function() {
+ Object.defineProperty(this, "nativeCrypto", {
+ configurable: false,
+ enumerable: false,
+ get: function(){
+ return _native_crypto;
+ }
+ });
+};
+
+Crypto.prototype = {
+
+ // case sensitive
+ sha256: function(data) {
+ if (typeof data !== "string") {
+ throw new Error("input must be string");
+ }
+ // any string
+ return this.nativeCrypto.sha256(data);
+ },
+
+ // case sensitive
+ sha3256: function(data) {
+ if (typeof data !== "string") {
+ throw new Error("input must be string");
+ }
+ // any string
+ return this.nativeCrypto.sha3256(data);
+ },
+
+ // case sensitive
+ ripemd160: function(data) {
+ if (typeof data !== "string") {
+ throw new Error("input must be string");
+ }
+ // any string
+ return this.nativeCrypto.ripemd160(data);
+ },
+
+ // case insensitive
+ recoverAddress: function(alg, hash, sign) {
+ if (!Number.isSafeInteger(alg) || alg < 0) {
+ throw new Error("alg must be non-negative integer");
+ }
+
+ if (typeof hash !== "string" || !HexStringRegex.test(hash)
+ || typeof sign !== "string" || !HexStringRegex.test(sign)) {
+ throw new Error("hash & sign must be hex string");
+ }
+ // alg: 1
+ // hash: sha3256 hex string, 64 chars
+ // sign: cipher hex string by private key, 130 chars
+ return this.nativeCrypto.recoverAddress(alg, hash, sign);
+ },
+
+ // case sensitive
+ md5: function(data) {
+ if (typeof data !== "string") {
+ throw new Error("input must be string");
+ }
+ // any string
+ return this.nativeCrypto.md5(data);
+ },
+
+ // case sensitive
+ base64: function(data) {
+ if (typeof data !== "string") {
+ throw new Error("input must be string");
+ }
+ // any string
+ return this.nativeCrypto.base64(data);
+ }
+};
+
+module.exports = new Crypto();
\ No newline at end of file
diff --git a/nf/nvm/v8/lib/1.0.5/date.js b/nf/nvm/v8/lib/1.0.5/date.js
new file mode 100644
index 000000000..663d48c53
--- /dev/null
+++ b/nf/nvm/v8/lib/1.0.5/date.js
@@ -0,0 +1,80 @@
+// Copyright (C) 2018 go-nebulas authors
+//
+// This file is part of the go-nebulas library.
+//
+// the go-nebulas library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU 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-nebulas 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with the go-nebulas library. If not, see .
+//
+
+
+var NebDate = (function(ProtoDate) {
+
+ function NebDate() {
+ if (!Blockchain) {
+ throw new Error("'Blockchain' is not defined.");
+ }
+ if (!Blockchain.block) {
+ throw new Error("'Blockchain.block' is not defined.");
+ }
+
+ var date = new(Function.prototype.bind.apply(ProtoDate, [ProtoDate].concat(Array.prototype.slice.call(arguments))))();
+ if (arguments.length == 0) {
+ // unit of timestamp is second
+ date.setTime(Blockchain.block.timestamp * 1000);
+ }
+ Object.setPrototypeOf(date, NebDate.prototype);
+ return date;
+ }
+ NebDate.now = function() {
+ return new NebDate().getTime();
+ }
+ NebDate.UTC = function() {
+ return ProtoDate.UTC.apply(null, Array.prototype.slice.call(arguments));
+ }
+ NebDate.parse = function(dateString) {
+ return ProtoDate.parse(dateString);
+ }
+
+ NebDate.prototype.getYear = function() {
+ throw new Error("Deprecated!");
+ }
+ NebDate.prototype.setYear = function() {
+ throw new Error("Deprecated!");
+ }
+
+ NebDate.prototype.toLocaleDateString = function() {
+ var tmp = new ProtoDate.prototype.constructor(this.getTime());
+ return ProtoDate.prototype.toLocaleDateString.apply(tmp, Array.prototype.slice.call(arguments));
+ }
+
+ NebDate.prototype.toLocaleTimeString = function() {
+ var tmp = new ProtoDate.prototype.constructor(this.getTime());
+ return ProtoDate.prototype.toLocaleTimeString.apply(tmp, Array.prototype.slice.call(arguments));
+ }
+
+ NebDate.prototype.toLocaleString = function() {
+ var tmp = new ProtoDate.prototype.constructor(this.getTime());
+ return ProtoDate.prototype.toLocaleString.apply(tmp, Array.prototype.slice.call(arguments));
+ }
+
+ NebDate.prototype = new Proxy(NebDate.prototype, {
+ getPrototypeOf: function(target) {
+ throw new Error("Unsupported method!");
+ },
+ });
+
+ Object.setPrototypeOf(NebDate.prototype, ProtoDate.prototype);
+ return NebDate;
+})(Date);
+
+module.exports = NebDate;
\ No newline at end of file
diff --git a/nf/nvm/v8/lib/1.0.5/execution_env.js b/nf/nvm/v8/lib/1.0.5/execution_env.js
new file mode 100644
index 000000000..2747a6146
--- /dev/null
+++ b/nf/nvm/v8/lib/1.0.5/execution_env.js
@@ -0,0 +1,112 @@
+// Copyright (C) 2017 go-nebulas authors
+//
+// This file is part of the go-nebulas library.
+//
+// the go-nebulas library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU 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-nebulas 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with the go-nebulas library. If not, see .
+//
+
+Function.prototype.toString = function(){return "";};
+
+const require = (function (global) {
+ var PathDoubleDotRegex = /\.{2}\//;
+ var modules = new Map();
+
+ var Module = function (id, parent) {
+ this.exports = {};
+ Object.defineProperty(this, "id", {
+ enumerable: false,
+ configurable: false,
+ writable: false,
+ value: id
+ });
+
+ if (parent && !(parent instanceof Module)) {
+ throw new Error("parent parameter of Module construction must be instance of Module or null.");
+ }
+ };
+
+ Module.prototype = {
+ _load: function () {
+ var $this = this,
+ native_req_func = _native_require(this.id),
+ temp_global = Object.create(global);
+ native_req_func.call(temp_global, this.exports, this, curry(require_func, $this));
+ },
+ _resolve: function (id) {
+ if (PathDoubleDotRegex.test(id)) {
+ throw new Error("invalid path '../'");
+ }
+ id = "lib/" + id;
+ var paths = [];
+
+ for (const p of id.split("/")) {
+ if (p == "" || p == ".") {
+ continue;
+ } else {
+ paths.push(p);
+ }
+ }
+
+ if (paths.length > 0 && paths[0] == "") {
+ paths.shift();
+ }
+
+ return paths.join("/");
+ },
+ };
+
+ var globalModule = new Module("main.js");
+ modules.set(globalModule.id, globalModule);
+
+ function require_func(parent, id) {
+ id = parent._resolve(id);
+ var module = modules.get(id);
+ if (!module || !(module instanceof Module)) {
+ module = new Module(id, parent);
+ module._load();
+ modules.set(id, module);
+ }
+ return module.exports;
+ };
+
+ function curry(uncurried) {
+ var parameters = Array.prototype.slice.call(arguments, 1);
+ var f = function () {
+ return uncurried.apply(this, parameters.concat(
+ Array.prototype.slice.call(arguments, 0)
+ ));
+ };
+ Object.defineProperty(f, "main", {
+ enumerable: true,
+ configurable: false,
+ writable: false,
+ value: globalModule,
+ });
+ return f;
+ };
+
+ return curry(require_func, globalModule);
+})(this);
+
+const console = require('console.js');
+const Event = require('event.js');
+const ContractStorage = require('storage.js');
+const LocalContractStorage = ContractStorage.lcs;
+const GlobalContractStorage = ContractStorage.gcs;
+const BigNumber = require('bignumber.js');
+const Uint = require('uint.js');
+const Blockchain = require('blockchain.js');
+
+var Date = require('date.js');
+Math.random = require('random.js');
\ No newline at end of file
diff --git a/nf/nvm/v8/lib/1.0.5/random.js b/nf/nvm/v8/lib/1.0.5/random.js
new file mode 100644
index 000000000..8bdf1c2a0
--- /dev/null
+++ b/nf/nvm/v8/lib/1.0.5/random.js
@@ -0,0 +1,134 @@
+// Copyright (C) 2018 go-nebulas
+//
+// This file is part of the go-nebulas library.
+//
+// the go-nebulas library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU 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-nebulas 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with the go-nebulas library. If not, see .
+//
+
+// A port of an algorithm by Johannes Baagøe , 2010
+// http://baagoe.com/en/RandomMusings/javascript/
+// https://github.com/nquinlan/better-random-numbers-for-javascript-mirror
+// Original work is under MIT license -
+
+// Other seeded random number generators for JavaScript, see https://github.com/davidbau/seedrandom.
+
+
+'use strict';
+
+function Alea(seed) {
+ var me = this, mash = Mash();
+
+ me.next = function () {
+ var t = 2091639 * me.s0 + me.c * 2.3283064365386963e-10; // 2^-32
+ me.s0 = me.s1;
+ me.s1 = me.s2;
+ return me.s2 = t - (me.c = t | 0);
+ };
+
+ // Apply the seeding algorithm from Baagoe.
+ me.c = 1;
+ me.s0 = mash(' ');
+ me.s1 = mash(' ');
+ me.s2 = mash(' ');
+ me.s0 -= mash(seed);
+ if (me.s0 < 0) { me.s0 += 1; }
+ me.s1 -= mash(seed);
+ if (me.s1 < 0) { me.s1 += 1; }
+ me.s2 -= mash(seed);
+ if (me.s2 < 0) { me.s2 += 1; }
+ mash = null;
+}
+
+function copy(f, t) {
+ t.c = f.c;
+ t.s0 = f.s0;
+ t.s1 = f.s1;
+ t.s2 = f.s2;
+ return t;
+}
+
+function impl(seed, opts) {
+ var xg = new Alea(seed),
+ state = opts && opts.state,
+ prng = xg.next;
+ prng.int32 = function () { return (xg.next() * 0x100000000) | 0; }
+ prng.double = function () {
+ return prng() + (prng() * 0x200000 | 0) * 1.1102230246251565e-16; // 2^-53
+ };
+ prng.quick = prng;
+ if (state) {
+ if (typeof (state) == 'object') copy(state, xg);
+ prng.state = function () { return copy(xg, {}); }
+ }
+ return prng;
+}
+
+function Mash() {
+ var n = 0xefc8249d;
+
+ var mash = function (data) {
+ data = data.toString();
+ for (var i = 0; i < data.length; i++) {
+ n += data.charCodeAt(i);
+ var h = 0.02519603282416938 * n;
+ n = h >>> 0;
+ h -= n;
+ h *= n;
+ n = h >>> 0;
+ h -= n;
+ n += h * 0x100000000; // 2^32
+ }
+ return (n >>> 0) * 2.3283064365386963e-10; // 2^-32
+ };
+
+ return mash;
+}
+
+module.exports = (function(){
+
+ var arng = null;
+
+ function checkCtx() {
+ if (!Blockchain) {
+ throw new Error("'Blockchain' is undefined.");
+ }
+ if (!Blockchain.block) {
+ throw new Error("'Blockchain.block' is undefined.");
+ }
+
+ if (Blockchain.block.seed == null || typeof(Blockchain.block.seed) === 'undefined') {
+ throw new Error("Math.random func is not allowed in nvm.");
+ }
+ }
+
+ function rand() {
+ if (arng == null) {
+ checkCtx();
+ arng = new impl(Blockchain.block.seed);
+ }
+ return arng();
+ }
+ rand.seed = function(userseed) {
+ if (typeof(userseed) !== 'string') {
+ throw new Error("input seed must be a string")
+ }
+ if (userseed === "") {
+ return;
+ }
+ checkCtx();
+ arng = new impl(Blockchain.block.seed + userseed);
+ }
+
+ return rand;
+})();
\ No newline at end of file
diff --git a/nf/nvm/v8/lib/1.0.5/uint.js b/nf/nvm/v8/lib/1.0.5/uint.js
new file mode 100644
index 000000000..a9a2da6ce
--- /dev/null
+++ b/nf/nvm/v8/lib/1.0.5/uint.js
@@ -0,0 +1,181 @@
+// Copyright (C) 2018 go-nebulas authors
+//
+// This file is part of the go-nebulas library.
+//
+// the go-nebulas library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU 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-nebulas 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with the go-nebulas library. If not, see .
+//
+
+/*
+ * this module must be required after bignumber.js
+ */
+'use strict';
+
+/*
+ * ffffffffffffffff 18446744073709551615
+ * ffffffffffffffffffffffffffffffff 340282366920938463463374607431768211455
+ * ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 115792089237316195423570985008687907853269984665640564039457584007913129639935
+ * ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 13407807929942597099574024998205846127479365820592393377723561443721764030073546976801874298166903427690031858186486050853753882811946569946433649006084095
+ */
+const MAX_UINT64 = new BigNumber('ffffffffffffffff', 16);
+const MAX_UINT128 = new BigNumber('ffffffffffffffffffffffffffffffff', 16);
+const MAX_UINT256 = new BigNumber('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', 16);
+const MAX_UINT512 = new BigNumber('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', 16);
+
+const MAX_UINTS = {
+ 64: MAX_UINT64,
+ 128: MAX_UINT128,
+ 256: MAX_UINT256,
+ 512: MAX_UINT512
+}
+
+class Uint {
+ constructor(n, b, s) {
+ Object.defineProperties(this, {
+ _inner: {
+ value: new BigNumber(n, b)
+ },
+ _size: {
+ value: s
+ }
+ });
+
+ this._validate();
+ }
+
+ _validate() {
+ // check integer
+ if (!this._inner.isInteger()) {
+ throw new Error('[Uint' + this._size + ' Error] not an integer');
+ }
+
+ // check negative
+ if (this._inner.isNegative()) {
+ throw new Error('[Uint' + this._size + ' Error] underflow');
+ }
+
+ // check overflow
+ if (this._inner.gt(MAX_UINTS[this._size])) {
+ throw new Error('[Uint' + this._size + ' Error] overflow');
+ }
+ }
+
+ _checkRightOperand(right) {
+ if (typeof right === 'undefined' || right == null) {
+ throw new Error('[Uint' + this._size + ' Error] NaN');
+ }
+
+ if (!right instanceof Uint || this.constructor !== right.constructor) {
+ throw new Error('[Uint' + this._size + ' Error] incompatible type');
+ }
+ right._validate();
+ }
+
+ div(o) {
+ this._checkRightOperand(o);
+ var r = this._inner.divToInt(o._inner);
+ return new this.constructor(r, null, this._size);
+ }
+
+ pow(o) {
+ this._checkRightOperand(o);
+ var r = this._inner.pow(o._inner);
+ return new this.constructor(r, null, this._size);
+ }
+
+ minus(o) {
+ this._checkRightOperand(o);
+ var r = this._inner.minus(o._inner);
+ return new this.constructor(r, null, this._size);
+ }
+
+ mod(o) {
+ this._checkRightOperand(o);
+ var r = this._inner.mod(o._inner);
+ return new this.constructor(r, null, this._size);
+ }
+
+ mul(o) {
+ this._checkRightOperand(o);
+ var r = this._inner.times(o._inner);
+ return new this.constructor(r, null, this._size);
+ }
+
+ plus(o) {
+ this._checkRightOperand(o);
+ var r = this._inner.plus(o._inner);
+ return new this.constructor(r, null, this._size);
+ }
+
+ cmp(o) {
+ this._checkRightOperand(o);
+ return this._inner.comparedTo(o._inner);
+ }
+
+ isZero() {
+ return this._inner.isZero();
+ }
+
+ toString() {
+ return this._inner.toString.apply(this._inner, Array.prototype.slice.call(arguments));
+ }
+}
+
+class Uint64 extends Uint {
+ constructor(n, b) {
+ super(n, b, 64);
+ }
+
+ static get MaxValue () {
+ return new Uint64(MAX_UINTS[64], null, 64);
+ }
+}
+
+class Uint128 extends Uint {
+ constructor(n, b) {
+ super(n, b, 128);
+ }
+
+ static get MaxValue () {
+ return new Uint128(MAX_UINTS[128], null, 128);
+ }
+}
+
+class Uint256 extends Uint {
+ constructor(n, b) {
+ super(n, b, 256);
+ }
+
+ static get MaxValue () {
+ return new Uint256(MAX_UINTS[256], null, 256);
+ }
+}
+class Uint512 extends Uint {
+ constructor(n, b) {
+ super(n, b, 512);
+ }
+
+ static get MaxValue () {
+ return new Uint512(MAX_UINTS[512], null, 512);
+ }
+}
+
+module.exports = {
+ Uint64: Uint64,
+ Uint128: Uint128,
+ Uint256: Uint256,
+ Uint512: Uint512,
+ isUint: function(o) {
+ return o instanceof Uint;
+ }
+};
\ No newline at end of file
diff --git a/nf/nvm/v8/lib/blockchain.cc b/nf/nvm/v8/lib/blockchain.cc
index 961ecfcae..99a38a805 100644
--- a/nf/nvm/v8/lib/blockchain.cc
+++ b/nf/nvm/v8/lib/blockchain.cc
@@ -18,21 +18,30 @@
//
#include "blockchain.h"
+#include "global.h"
#include "../engine.h"
#include "instruction_counter.h"
+#include "logger.h"
+#include "limits.h"
static GetTxByHashFunc sGetTxByHash = NULL;
static GetAccountStateFunc sGetAccountState = NULL;
static TransferFunc sTransfer = NULL;
static VerifyAddressFunc sVerifyAddress = NULL;
+static GetPreBlockHashFunc sGetPreBlockHash = NULL;
+static GetPreBlockSeedFunc sGetPreBlockSeed = NULL;
void InitializeBlockchain(GetTxByHashFunc getTx, GetAccountStateFunc getAccount,
TransferFunc transfer,
- VerifyAddressFunc verifyAddress) {
+ VerifyAddressFunc verifyAddress,
+ GetPreBlockHashFunc getPreBlockHash,
+ GetPreBlockSeedFunc getPreBlockSeed) {
sGetTxByHash = getTx;
sGetAccountState = getAccount;
sTransfer = transfer;
sVerifyAddress = verifyAddress;
+ sGetPreBlockHash = getPreBlockHash;
+ sGetPreBlockSeed = getPreBlockSeed;
}
void NewBlockchainInstance(Isolate *isolate, Local context,
@@ -47,13 +56,11 @@ void NewBlockchainInstance(Isolate *isolate, Local context,
PropertyAttribute::ReadOnly));
*/
- /* disable getAccountState() function.
- blockTpl->Set(String::NewFromUtf8(isolate, "getAccountState"),
- FunctionTemplate::New(isolate, GetAccountStateCallback),
- static_cast(PropertyAttribute::DontDelete
- |
- PropertyAttribute::ReadOnly));
- */
+ blockTpl->Set(String::NewFromUtf8(isolate, "getAccountState"),
+ FunctionTemplate::New(isolate, GetAccountStateCallback),
+ static_cast(PropertyAttribute::DontDelete|
+ PropertyAttribute::ReadOnly));
+
blockTpl->Set(String::NewFromUtf8(isolate, "transfer"),
FunctionTemplate::New(isolate, TransferCallback),
@@ -65,6 +72,16 @@ void NewBlockchainInstance(Isolate *isolate, Local context,
static_cast(PropertyAttribute::DontDelete |
PropertyAttribute::ReadOnly));
+ blockTpl->Set(String::NewFromUtf8(isolate, "getPreBlockHash"),
+ FunctionTemplate::New(isolate, GetPreBlockHashCallback),
+ static_cast(PropertyAttribute::DontDelete |
+ PropertyAttribute::ReadOnly));
+
+ blockTpl->Set(String::NewFromUtf8(isolate, "getPreBlockSeed"),
+ FunctionTemplate::New(isolate, GetPreBlockSeedCallback),
+ static_cast(PropertyAttribute::DontDelete |
+ PropertyAttribute::ReadOnly));
+
Local