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 instance = blockTpl->NewInstance(context).ToLocalChecked(); instance->SetInternalField(0, External::New(isolate, handler)); @@ -109,9 +126,13 @@ void GetTransactionByHashCallback(const FunctionCallbackInfo &info) { // GetAccountStateCallback void GetAccountStateCallback(const FunctionCallbackInfo &info) { + int err = NVM_SUCCESS; Isolate *isolate = info.GetIsolate(); + if (NULL == isolate) { + LogFatalf("Unexpected error: failed to get ioslate"); + } Local thisArg = info.Holder(); - Local handler = Local::Cast(thisArg->GetInternalField(0)); + Local handler = Local::Cast(thisArg->GetInternalField(0));//TODO: if (info.Length() != 1) { isolate->ThrowException(String::NewFromUtf8( @@ -121,19 +142,26 @@ void GetAccountStateCallback(const FunctionCallbackInfo &info) { Local key = info[0]; if (!key->IsString()) { - isolate->ThrowException(String::NewFromUtf8(isolate, "key must be string")); + isolate->ThrowException(String::NewFromUtf8(isolate, "Blockchain.getAccountState(), argument must be a string")); return; } + size_t cnt = 0; + char *result = NULL; + char *exceptionInfo = NULL; + err = sGetAccountState(handler->Value(), *String::Utf8Value(key->ToString()), &cnt, &result, &exceptionInfo); - char *value = sGetAccountState(handler->Value(), - *String::Utf8Value(key->ToString()), &cnt); - if (value == NULL) { - info.GetReturnValue().SetNull(); - } else { - info.GetReturnValue().Set(String::NewFromUtf8(isolate, value)); - free(value); + DEAL_ERROR_FROM_GOLANG(err); + + if (result != NULL) { + free(result); + result = NULL; + } + + if (exceptionInfo != NULL) { + free(exceptionInfo); + exceptionInfo = NULL; } // record storage usage. @@ -204,3 +232,117 @@ void VerifyAddressCallback(const FunctionCallbackInfo &info) { // record storage usage. IncrCounter(isolate, isolate->GetCurrentContext(), cnt); } + +// GetPreBlockHashCallBack +void GetPreBlockHashCallback(const FunctionCallbackInfo &info) { + int err = NVM_SUCCESS; + Isolate *isolate = info.GetIsolate(); + if (NULL == isolate) { + LogFatalf("Unexpected error: failed to get isolate"); + } + Local thisArg = info.Holder(); + Local handler = Local::Cast(thisArg->GetInternalField(0)); + + if (info.Length() != 1) { + isolate->ThrowException(String::NewFromUtf8( + isolate, "Blockchain.GetPreBlockHash() requires 1 arguments")); + return; + } + + Local offset = info[0]; + if (!offset->IsNumber()) { + isolate->ThrowException( + String::NewFromUtf8(isolate, "Blockchain.GetPreBlockHash(), the argument must be a number")); + return; + } + + double v = Number::Cast(*offset)->Value(); + if (v > ULLONG_MAX || v <= 0) { + isolate->ThrowException( + String::NewFromUtf8(isolate, "Blockchain.GetPreBlockHash(), argument out of range")); + return; + } + + if (v != (double)(unsigned long long)v) { + isolate->ThrowException( + String::NewFromUtf8(isolate, "Blockchain.GetPreBlockHash(), argument must be integer")); + return; + } + + size_t cnt = 0; + char *result = NULL; + char *exceptionInfo = NULL; + err = sGetPreBlockHash(handler->Value(), (unsigned long long)(v), &cnt, &result, &exceptionInfo); + + DEAL_ERROR_FROM_GOLANG(err); + + if (result != NULL) { + free(result); + result = NULL; + } + + if (exceptionInfo != NULL) { + free(exceptionInfo); + exceptionInfo = NULL; + } + + // record storage usage. + IncrCounter(isolate, isolate->GetCurrentContext(), cnt); +} + +// GetPreBlockSeedCallBack +void GetPreBlockSeedCallback(const FunctionCallbackInfo &info) { + int err = NVM_SUCCESS; + Isolate *isolate = info.GetIsolate(); + if (NULL == isolate) { + LogFatalf("Unexpected error: failed to get isolate"); + } + Local thisArg = info.Holder(); + Local handler = Local::Cast(thisArg->GetInternalField(0)); + + if (info.Length() != 1) { + isolate->ThrowException(String::NewFromUtf8( + isolate, "Blockchain.GetPreBlockSeed() requires 1 arguments")); + return; + } + + Local offset = info[0]; + if (!offset->IsNumber()) { + isolate->ThrowException( + String::NewFromUtf8(isolate, "Blockchain.GetPreBlockSeed(), the argument must be a number")); + return; + } + + double v = Number::Cast(*offset)->Value(); + if (v > ULLONG_MAX || v <= 0) { + isolate->ThrowException( + String::NewFromUtf8(isolate, "Blockchain.GetPreBlockSeed(), argument out of range")); + return; + } + + if (v != (double)(unsigned long long)v) { + isolate->ThrowException( + String::NewFromUtf8(isolate, "Blockchain.GetPreBlockSeed(), argument must be integer")); + return; + } + + size_t cnt = 0; + char *result = NULL; + char *exceptionInfo = NULL; + err = sGetPreBlockSeed(handler->Value(), (unsigned long long)(v), &cnt, &result, &exceptionInfo); + + DEAL_ERROR_FROM_GOLANG(err); + + if (result != NULL) { + free(result); + result = NULL; + } + + if (exceptionInfo != NULL) { + free(exceptionInfo); + exceptionInfo = NULL; + } + + // record storage usage. + IncrCounter(isolate, isolate->GetCurrentContext(), cnt); +} diff --git a/nf/nvm/v8/lib/blockchain.h b/nf/nvm/v8/lib/blockchain.h index 000243ee2..110b809bf 100644 --- a/nf/nvm/v8/lib/blockchain.h +++ b/nf/nvm/v8/lib/blockchain.h @@ -32,5 +32,8 @@ void GetTransactionByHashCallback(const FunctionCallbackInfo &info); void GetAccountStateCallback(const FunctionCallbackInfo &info); void TransferCallback(const FunctionCallbackInfo &info); void VerifyAddressCallback(const FunctionCallbackInfo &info); +void GetPreBlockHashCallback(const FunctionCallbackInfo &info); +void GetPreBlockSeedCallback(const FunctionCallbackInfo &info); + #endif //_NEBULAS_NF_NVM_V8_LIB_BLOCKCHAIN_H_ diff --git a/nf/nvm/v8/lib/crypto.cc b/nf/nvm/v8/lib/crypto.cc new file mode 100644 index 000000000..fd5686f8b --- /dev/null +++ b/nf/nvm/v8/lib/crypto.cc @@ -0,0 +1,279 @@ +// 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 . +// + +#include "crypto.h" +#include "../engine.h" +#include "instruction_counter.h" + +static Sha256Func sSha256 = NULL; +static Sha3256Func sSha3256 = NULL; +static Ripemd160Func sRipemd160 = NULL; +static RecoverAddressFunc sRecoverAddress = NULL; +static Md5Func sMd5 = NULL; +static Base64Func sBase64 = NULL; + +void InitializeCrypto(Sha256Func sha256, + Sha3256Func sha3256, + Ripemd160Func ripemd160, + RecoverAddressFunc recoverAddress, + Md5Func md5, + Base64Func base64) { + sSha256 = sha256; + sSha3256 = sha3256; + sRipemd160 = ripemd160; + sRecoverAddress = recoverAddress; + sMd5 = md5; + sBase64 = base64; +} + +void NewCryptoInstance(Isolate *isolate, Local context) { + Local cryptoTpl = ObjectTemplate::New(isolate); + + cryptoTpl->Set(String::NewFromUtf8(isolate, "sha256"), + FunctionTemplate::New(isolate, Sha256Callback), + static_cast(PropertyAttribute::DontDelete | + PropertyAttribute::ReadOnly)); + + cryptoTpl->Set(String::NewFromUtf8(isolate, "sha3256"), + FunctionTemplate::New(isolate, Sha3256Callback), + static_cast(PropertyAttribute::DontDelete | + PropertyAttribute::ReadOnly)); + + cryptoTpl->Set(String::NewFromUtf8(isolate, "ripemd160"), + FunctionTemplate::New(isolate, Ripemd160Callback), + static_cast(PropertyAttribute::DontDelete | + PropertyAttribute::ReadOnly)); + + cryptoTpl->Set(String::NewFromUtf8(isolate, "recoverAddress"), + FunctionTemplate::New(isolate, RecoverAddressCallback), + static_cast(PropertyAttribute::DontDelete | + PropertyAttribute::ReadOnly)); + + cryptoTpl->Set(String::NewFromUtf8(isolate, "md5"), + FunctionTemplate::New(isolate, Md5Callback), + static_cast(PropertyAttribute::DontDelete | + PropertyAttribute::ReadOnly)); + + cryptoTpl->Set(String::NewFromUtf8(isolate, "base64"), + FunctionTemplate::New(isolate, Base64Callback), + static_cast(PropertyAttribute::DontDelete | + PropertyAttribute::ReadOnly)); + + Local instance = cryptoTpl->NewInstance(context).ToLocalChecked(); + + context->Global()->DefineOwnProperty( + context, String::NewFromUtf8(isolate, "_native_crypto"), instance, + static_cast(PropertyAttribute::DontDelete | + PropertyAttribute::ReadOnly)); +} + +// Sha256Callback +void Sha256Callback(const FunctionCallbackInfo &info) { + Isolate *isolate = info.GetIsolate(); + + if (info.Length() != 1) { + isolate->ThrowException(String::NewFromUtf8( + isolate, "sha256() requires only 1 argument")); + return; + } + + Local data = info[0]; + if (!data->IsString()) { + isolate->ThrowException(String::NewFromUtf8(isolate, "sha256() requires a string argument")); + return; + } + + size_t cnt = 0; + + char *value = sSha256(*String::Utf8Value(data->ToString()), &cnt); + if (value == NULL) { + info.GetReturnValue().SetNull(); + } else { + info.GetReturnValue().Set(String::NewFromUtf8(isolate, value)); + free(value); + } + + // record storage usage. + IncrCounter(isolate, isolate->GetCurrentContext(), cnt); +} + +// Sha3256Callback +void Sha3256Callback(const FunctionCallbackInfo &info) { + Isolate *isolate = info.GetIsolate(); + + if (info.Length() != 1) { + isolate->ThrowException(String::NewFromUtf8( + isolate, "sha3256() requires only 1 argument")); + return; + } + + Local data = info[0]; + if (!data->IsString()) { + isolate->ThrowException(String::NewFromUtf8(isolate, "sha3256() requires a string argument")); + return; + } + + size_t cnt = 0; + + char *value = sSha3256(*String::Utf8Value(data->ToString()), &cnt); + if (value == NULL) { + info.GetReturnValue().SetNull(); + } else { + info.GetReturnValue().Set(String::NewFromUtf8(isolate, value)); + free(value); + } + + // record storage usage. + IncrCounter(isolate, isolate->GetCurrentContext(), cnt); +} + +// Ripemd160Callback +void Ripemd160Callback(const FunctionCallbackInfo &info) { + Isolate *isolate = info.GetIsolate(); + + if (info.Length() != 1) { + isolate->ThrowException(String::NewFromUtf8( + isolate, "ripemd160() requires only 1 argument")); + return; + } + + Local data = info[0]; + if (!data->IsString()) { + isolate->ThrowException(String::NewFromUtf8(isolate, "ripemd160() requires a string argument")); + return; + } + + size_t cnt = 0; + + char *value = sRipemd160(*String::Utf8Value(data->ToString()), &cnt); + if (value == NULL) { + info.GetReturnValue().SetNull(); + } else { + info.GetReturnValue().Set(String::NewFromUtf8(isolate, value)); + free(value); + } + + // record storage usage. + IncrCounter(isolate, isolate->GetCurrentContext(), cnt); +} + +// RecoverAddressCallback +void RecoverAddressCallback(const FunctionCallbackInfo &info) { + Isolate *isolate = info.GetIsolate(); + + if (info.Length() != 3) { + isolate->ThrowException(String::NewFromUtf8( + isolate, "recoverAddress() requires 3 arguments")); + return; + } + + Local alg = info[0]; + if (!alg->IsInt32()) { + isolate->ThrowException( + String::NewFromUtf8(isolate, "recoverAddress(): 1st arg should be integer")); + return; + } + + Local data = info[1]; + if (!data->IsString()) { + isolate->ThrowException( + String::NewFromUtf8(isolate, "recoverAddress(): 2nd arg should be string")); + return; + } + + Local sign = info[2]; + if (!sign->IsString()) { + isolate->ThrowException( + String::NewFromUtf8(isolate, "recoverAddress(): 3rd arg should be string")); + return; + } + + size_t cnt = 0; + + char *value = sRecoverAddress(alg->ToInt32()->Int32Value(), *String::Utf8Value(data->ToString()), + *String::Utf8Value(sign->ToString()), &cnt); + if (value == NULL) { + info.GetReturnValue().SetNull(); + } else { + info.GetReturnValue().Set(String::NewFromUtf8(isolate, value)); + free(value); + } + + // record storage usage. + IncrCounter(isolate, isolate->GetCurrentContext(), cnt); +} + +// Md5Callback +void Md5Callback(const FunctionCallbackInfo &info) { + Isolate *isolate = info.GetIsolate(); + + if (info.Length() != 1) { + isolate->ThrowException(String::NewFromUtf8( + isolate, "md5() requires only 1 argument")); + return; + } + + Local data = info[0]; + if (!data->IsString()) { + isolate->ThrowException(String::NewFromUtf8(isolate, "md5() requires a string argument")); + return; + } + + size_t cnt = 0; + + char *value = sMd5(*String::Utf8Value(data->ToString()), &cnt); + if (value == NULL) { + info.GetReturnValue().SetNull(); + } else { + info.GetReturnValue().Set(String::NewFromUtf8(isolate, value)); + free(value); + } + + // record storage usage. + IncrCounter(isolate, isolate->GetCurrentContext(), cnt); +} + +// Base64Callback +void Base64Callback(const FunctionCallbackInfo &info) { + Isolate *isolate = info.GetIsolate(); + + if (info.Length() != 1) { + isolate->ThrowException(String::NewFromUtf8( + isolate, "base64() requires only 1 argument")); + return; + } + + Local data = info[0]; + if (!data->IsString()) { + isolate->ThrowException(String::NewFromUtf8(isolate, "base64() requires a string argument")); + return; + } + + size_t cnt = 0; + + char *value = sBase64(*String::Utf8Value(data->ToString()), &cnt); + if (value == NULL) { + info.GetReturnValue().SetNull(); + } else { + info.GetReturnValue().Set(String::NewFromUtf8(isolate, value)); + free(value); + } + + // record storage usage. + IncrCounter(isolate, isolate->GetCurrentContext(), cnt); +} \ No newline at end of file diff --git a/nf/nvm/v8/lib/crypto.h b/nf/nvm/v8/lib/crypto.h new file mode 100644 index 000000000..d58826003 --- /dev/null +++ b/nf/nvm/v8/lib/crypto.h @@ -0,0 +1,35 @@ +// 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 . +// + +#ifndef _NEBULAS_NF_NVM_V8_LIB_CRYPTO_H_ +#define _NEBULAS_NF_NVM_V8_LIB_CRYPTO_H_ + +#include + +using namespace v8; + +void NewCryptoInstance(Isolate *isolate, Local context); + +void Sha256Callback(const FunctionCallbackInfo &info); +void Sha3256Callback(const FunctionCallbackInfo &info); +void Ripemd160Callback(const FunctionCallbackInfo &info); +void RecoverAddressCallback(const FunctionCallbackInfo &info); +void Md5Callback(const FunctionCallbackInfo &info); +void Base64Callback(const FunctionCallbackInfo &info); + +#endif //_NEBULAS_NF_NVM_V8_LIB_CRYPTO_H_ \ No newline at end of file diff --git a/nf/nvm/v8/lib/execution_env.cc b/nf/nvm/v8/lib/execution_env.cc index d2a81d4e6..637a2118b 100644 --- a/nf/nvm/v8/lib/execution_env.cc +++ b/nf/nvm/v8/lib/execution_env.cc @@ -18,11 +18,29 @@ // #include "execution_env.h" +#include "../engine.h" #include "file.h" #include "logger.h" +#include "global.h" +#include "string.h" +static AttachLibVersionDelegate alvDelegate = NULL; int SetupExecutionEnv(Isolate *isolate, Local &context) { - char *data = readFile("lib/execution_env.js", NULL); + char *verlib = NULL; + if (alvDelegate != NULL) { + V8Engine *e = GetV8EngineInstance(context); + verlib = alvDelegate(e, "lib/execution_env.js"); + } + if (verlib == NULL) { + return 1; + } + + char path[64] = {0}; + strcat(path, verlib); + free(verlib); + + char *data = readFile(path, NULL); + // char *data = readFile("lib/execution_env.js", NULL); if (data == NULL) { isolate->ThrowException(Exception::Error( String::NewFromUtf8(isolate, "execution_env.js is not found."))); @@ -52,3 +70,7 @@ int SetupExecutionEnv(Isolate *isolate, Local &context) { return 0; } + +void InitializeExecutionEnvDelegate(AttachLibVersionDelegate aDelegate) { + alvDelegate = aDelegate; +} diff --git a/nf/nvm/v8/lib/fake_blockchain.cc b/nf/nvm/v8/lib/fake_blockchain.cc index 9308d7fce..02d85b551 100644 --- a/nf/nvm/v8/lib/fake_blockchain.cc +++ b/nf/nvm/v8/lib/fake_blockchain.cc @@ -24,6 +24,7 @@ #include #include +#include "nvm_error.h" using namespace std; @@ -40,22 +41,32 @@ char *GetTxByHash(void *handler, const char *hash, size_t *gasCnt) { return ret; } -char *GetAccountState(void *handler, const char *address, size_t *gasCnt) { +int GetAccountState(void *handler, const char *address, size_t *gasCnt, char **result, char **info) { *gasCnt = 1000; - char *ret = NULL; string value = "{\"value\":1,\"nonce\":4}"; - ret = (char *)calloc(value.length() + 1, sizeof(char)); - strncpy(ret, value.c_str(), value.length()); - return ret; + *result = (char *)calloc(value.length() + 1, sizeof(char)); + strncpy(*result, value.c_str(), value.length()); + return NVM_SUCCESS; } int Transfer(void *handler, const char *to, const char *value, size_t *gasCnt) { *gasCnt = 2000; - return 0; + return NVM_SUCCESS; } int VerifyAddress(void *handler, const char *address, size_t *gasCnt) { *gasCnt = 100; - return 0; + return NVM_SUCCESS; } + +int GetPreBlockHash(void *handler, unsigned long long offset, size_t *gasCnt, char **result, char **info) { + *gasCnt = 1000; + return NVM_SUCCESS; +} + +int GetPreBlockSeed(void *handler, unsigned long long offset, size_t *gasCnt, char **result, char **info) { + *gasCnt = 1000; + return NVM_SUCCESS; +} + diff --git a/nf/nvm/v8/lib/fake_blockchain.h b/nf/nvm/v8/lib/fake_blockchain.h index a526eaf82..5df566646 100644 --- a/nf/nvm/v8/lib/fake_blockchain.h +++ b/nf/nvm/v8/lib/fake_blockchain.h @@ -23,8 +23,11 @@ #include char *GetTxByHash(void *handler, const char *hash, size_t *gasCnt); -char *GetAccountState(void *handler, const char *addres, size_t *gasCnts); +int GetAccountState(void *handler, const char *addres, size_t *gasCnts, char **result, char **info); int Transfer(void *handler, const char *to, const char *value, size_t *gasCnt); int VerifyAddress(void *handler, const char *address, size_t *gasCnt); +int GetPreBlockHash(void *handler, unsigned long long offset, size_t *counterVal, char **result, char **info); +int GetPreBlockSeed(void *handler, unsigned long long offset, size_t *counterVal, char **result, char **info); + #endif //_NEBULAS_NF_NVM_V8_LIB_FAKE_BLOCKCHAIN_H_ diff --git a/nf/nvm/v8/lib/file.cc b/nf/nvm/v8/lib/file.cc index a4a9e79ba..ed1428bda 100644 --- a/nf/nvm/v8/lib/file.cc +++ b/nf/nvm/v8/lib/file.cc @@ -77,14 +77,12 @@ bool isFile(const char *file) { } bool getCurAbsolute(char *curCwd, int len) { - char tmp[MAX_PATH_LEN] = {0}; - if (!getcwd(tmp, MAX_PATH_LEN)) { + char tmp[MAX_VERSIONED_PATH_LEN] = {0}; + if (!getcwd(tmp, MAX_VERSIONED_PATH_LEN)) { return false; } - strncat(tmp, LIB_DIR, MAX_PATH_LEN - strlen(tmp) - 1); - //staged ln - strncat(tmp, EXECUTION_FILE, MAX_PATH_LEN - strlen(tmp) -1); + strncat(tmp, "/lib/1.0.0/execution_env.js", MAX_VERSIONED_PATH_LEN - strlen(tmp) - 1); char *pc = realpath(tmp, NULL); if (pc == NULL) { @@ -95,9 +93,9 @@ bool getCurAbsolute(char *curCwd, int len) { free(pc); return false; } - memcpy(curCwd, pc, pcLen - strlen(EXECUTION_FILE)); + memcpy(curCwd, pc, pcLen - strlen("/1.0.0/execution_env.js")); //strncpy(curCwd, pc, len - 1); - curCwd[pcLen - strlen(EXECUTION_FILE)] = 0x00; + curCwd[pcLen - strlen("/1.0.0/execution_env.js")] = 0x00; free(pc); return true; } \ No newline at end of file diff --git a/nf/nvm/v8/lib/file.h b/nf/nvm/v8/lib/file.h index b4b5a77fe..be36e972f 100644 --- a/nf/nvm/v8/lib/file.h +++ b/nf/nvm/v8/lib/file.h @@ -23,11 +23,15 @@ #include #define MAX_PATH_LEN 1024 +#define MAX_VERSION_LEN 64 +#define MAX_VERSIONED_PATH_LEN 1088 #ifdef _WIN32 + #define FILE_SEPARATOR "\\" #define LIB_DIR "\\lib" #define EXECUTION_FILE "\\execution_env.js" #else + #define FILE_SEPARATOR "/" #define LIB_DIR "/lib" #define EXECUTION_FILE "/execution_env.js" #endif //WIN32 diff --git a/nf/nvm/v8/lib/global.cc b/nf/nvm/v8/lib/global.cc index 907b5f657..de7e86648 100644 --- a/nf/nvm/v8/lib/global.cc +++ b/nf/nvm/v8/lib/global.cc @@ -24,6 +24,7 @@ #include "log_callback.h" #include "require_callback.h" #include "storage_object.h" +#include "crypto.h" Local CreateGlobalObjectTemplate(Isolate *isolate) { Local globalTpl = ObjectTemplate::New(isolate); @@ -49,6 +50,7 @@ void SetGlobalObjectProperties(Isolate *isolate, Local context, NewInstructionCounterInstance(isolate, context, &(e->stats.count_of_executed_instructions), e); NewBlockchainInstance(isolate, context, lcsHandler); + NewCryptoInstance(isolate, context); } V8Engine *GetV8EngineInstance(Local context) { diff --git a/nf/nvm/v8/lib/logger.h b/nf/nvm/v8/lib/logger.h index 15a9d4759..da37d0410 100644 --- a/nf/nvm/v8/lib/logger.h +++ b/nf/nvm/v8/lib/logger.h @@ -24,6 +24,6 @@ void LogInfof(const char *format, ...); void LogErrorf(const char *format, ...); void LogDebugf(const char *format, ...); void LogWarnf(const char *format, ...); -void LogFatalF(const char *format, ...); +void LogFatalf(const char *format, ...); #endif // _NEBULAS_NF_NVM_V8_LOGGER_H_ diff --git a/nf/nvm/v8/lib/nvm_error.h b/nf/nvm/v8/lib/nvm_error.h new file mode 100644 index 000000000..59b3d4ac7 --- /dev/null +++ b/nf/nvm/v8/lib/nvm_error.h @@ -0,0 +1,63 @@ +// 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 +// . +// + +#ifndef _NEBULAS_NF_NVM_V8_ERROR_H_ +#define _NEBULAS_NF_NVM_V8_ERROR_H_ + +/* +success or crash +*/ +#define REPORT_UNEXPECTED_ERR() do{ \ + if (NULL == isolate) {\ + LogFatalf("Unexpected Error: invalid argument, ioslate is NULL");\ + }\ + Local context = isolate->GetCurrentContext();\ + V8Engine *e = GetV8EngineInstance(context);\ + if (NULL == e) {\ + LogFatalf("Unexpected Error: failed to get V8Engine");\ + }\ + TerminateExecution(e);\ + e->is_unexpected_error_happen = true;\ +} while(0) + +#define DEAL_ERROR_FROM_GOLANG(err) do {\ + if (NVM_UNEXPECTED_ERR == err || (NVM_EXCEPTION_ERR == err && NULL == exceptionInfo) ||\ + (NVM_SUCCESS == err && NULL == result)) {\ + info.GetReturnValue().SetNull();\ + REPORT_UNEXPECTED_ERR();\ + } else if (NVM_EXCEPTION_ERR == err) {\ + isolate->ThrowException(String::NewFromUtf8(isolate, exceptionInfo));\ + } else if (NVM_SUCCESS == err) {\ + info.GetReturnValue().Set(String::NewFromUtf8(isolate, result));\ + } else {\ + info.GetReturnValue().SetNull();\ + REPORT_UNEXPECTED_ERR();\ + }\ +} while(0) + +enum nvmErrno { + NVM_SUCCESS = 0, + NVM_EXCEPTION_ERR = -1, + NVM_MEM_LIMIT_ERR = -2, + NVM_GAS_LIMIT_ERR = -3, + NVM_UNEXPECTED_ERR = -4, + NVM_EXE_TIMEOUT_ERR = -5, +}; + +#endif //_NEBULAS_NF_NVM_V8_ENGINE_ERROR_H_ \ No newline at end of file diff --git a/nf/nvm/v8/lib/require_callback.cc b/nf/nvm/v8/lib/require_callback.cc index 6761cea0f..4f21d8436 100644 --- a/nf/nvm/v8/lib/require_callback.cc +++ b/nf/nvm/v8/lib/require_callback.cc @@ -39,6 +39,7 @@ static char source_require_format[] = "})();\n"; static RequireDelegate sRequireDelegate = NULL; +static AttachLibVersionDelegate attachLibVersionDelegate = NULL; static int readSource(Local context, const char *filename, char **data, size_t *lineOffset) { @@ -71,6 +72,19 @@ static int readSource(Local context, const char *filename, char **data, return 0; } +static void attachVersion(char *out, int maxoutlen, Local context, const char *libname) { + + char *verlib = NULL; + if (attachLibVersionDelegate != NULL) { + V8Engine *e = GetV8EngineInstance(context); + verlib = attachLibVersionDelegate(e, libname); + } + if (verlib != NULL) { + strncat(out, verlib, maxoutlen - strlen(out) - 1); + free(verlib); + } +} + void NewNativeRequireFunction(Isolate *isolate, Local globalTpl) { globalTpl->Set(String::NewFromUtf8(isolate, "_native_require"), @@ -104,20 +118,22 @@ void RequireCallback(const v8::FunctionCallbackInfo &info) { } char *abPath = NULL; if (strcmp(*filename, LIB_WHITE)) { // if needed, check array instead. - abPath = realpath(*filename, NULL); + char versionlizedPath[MAX_VERSIONED_PATH_LEN] = {0}; + attachVersion(versionlizedPath, MAX_VERSIONED_PATH_LEN, context, *filename); + abPath = realpath(versionlizedPath, NULL); if (abPath == NULL) { isolate->ThrowException(Exception::Error(String::NewFromUtf8( isolate, "require path is invalid absolutepath"))); return; } - static char curPath[MAX_PATH_LEN] = {0}; - if (curPath[0] == 0x00 && !getCurAbsolute(curPath, MAX_PATH_LEN)) { + char curPath[MAX_VERSIONED_PATH_LEN] = {0}; + if (curPath[0] == 0x00 && !getCurAbsolute(curPath, MAX_VERSIONED_PATH_LEN)) { isolate->ThrowException(Exception::Error( String::NewFromUtf8(isolate, "invalid cwd absolutepath"))); free(abPath); return; } - static int curLen = strlen(curPath); + int curLen = strlen(curPath); if (strncmp(abPath, curPath, curLen) != 0) { isolate->ThrowException(Exception::Error( String::NewFromUtf8(isolate, "require path is not in lib"))); @@ -163,6 +179,7 @@ void RequireCallback(const v8::FunctionCallbackInfo &info) { free(static_cast(data)); } -void InitializeRequireDelegate(RequireDelegate delegate) { +void InitializeRequireDelegate(RequireDelegate delegate, AttachLibVersionDelegate aDelegate) { sRequireDelegate = delegate; + attachLibVersionDelegate = aDelegate; } diff --git a/nf/nvm/v8/samples/main.cc b/nf/nvm/v8/samples/main.cc index 4828bcb8b..32698d76a 100644 --- a/nf/nvm/v8/samples/main.cc +++ b/nf/nvm/v8/samples/main.cc @@ -235,9 +235,10 @@ int main(int argc, const char *argv[]) { Initialize(); InitializeLogger(logFunc); - InitializeRequireDelegate(RequireDelegateFunc); + InitializeRequireDelegate(RequireDelegateFunc, AttachLibVersionDelegateFunc); + InitializeExecutionEnvDelegate(AttachLibVersionDelegateFunc); InitializeStorage(StorageGet, StoragePut, StorageDel); - InitializeBlockchain(GetTxByHash, GetAccountState, Transfer, VerifyAddress); + InitializeBlockchain(GetTxByHash, GetAccountState, Transfer, VerifyAddress, GetPreBlockHash, GetPreBlockSeed); InitializeEvent(eventTriggerFunc); int argcIdx = 1; diff --git a/nf/nvm/v8/samples/memory_modules.cc b/nf/nvm/v8/samples/memory_modules.cc index 18491b0b5..bd1c5feab 100644 --- a/nf/nvm/v8/samples/memory_modules.cc +++ b/nf/nvm/v8/samples/memory_modules.cc @@ -99,6 +99,17 @@ char *RequireDelegateFunc(void *handler, const char *filepath, return ret; } +char *AttachLibVersionDelegateFunc(void *handler, const char *libname) { + char *path = (char *)calloc(128, sizeof(char)); + if (strncmp(libname, "lib/", 4) == 0) { + sprintf(path, "lib/1.0.0/%s", libname + 4); + } else { + sprintf(path, "1.0.0/%s", libname); + } + LogDebugf("AttachLibVersion: %s -> %s", libname, path); + return path; +} + void AddModule(void *handler, const char *filename, const char *source, int lineOffset) { char filepath[128]; diff --git a/nf/nvm/v8/samples/memory_modules.h b/nf/nvm/v8/samples/memory_modules.h index 99135083b..05fc6c3a6 100644 --- a/nf/nvm/v8/samples/memory_modules.h +++ b/nf/nvm/v8/samples/memory_modules.h @@ -25,6 +25,8 @@ char *RequireDelegateFunc(void *handler, const char *filename, size_t *lineOffset); +char *AttachLibVersionDelegateFunc(void *handler, const char *libname); + void AddModule(void *handler, const char *filename, const char *source, int lineOffset); diff --git a/nf/nvm/v8/thread_engine.cc b/nf/nvm/v8/thread_engine.cc index 716c15409..4a727ee4b 100644 --- a/nf/nvm/v8/thread_engine.cc +++ b/nf/nvm/v8/thread_engine.cc @@ -22,6 +22,7 @@ #include "lib/tracing.h" #include "lib/typescript.h" #include "lib/logger.h" +#include "lib/nvm_error.h" #include #include @@ -31,10 +32,7 @@ #include #include #include -#define KillTimeMicros 1000 * 1000 * 5 #define MicroSecondDiff(newtv, oldtv) (1000000 * (unsigned long long)((newtv).tv_sec - (oldtv).tv_sec) + (newtv).tv_usec - (oldtv).tv_usec) //微秒 -#define CodeExecuteErr 1 -#define CodeTimeOut 2 void SetRunScriptArgs(v8ThreadContext *ctx, V8Engine *e, int opt, const char *source, int line_offset, int allow_usage) { ctx->e = e; @@ -52,6 +50,7 @@ char *InjectTracingInstructionsThread(V8Engine *e, const char *source, SetRunScriptArgs(&ctx, e, INSTRUCTION, source, *source_line_offset, allow_usage); bool btn = CreateScriptThread(&ctx); if (btn == false) { + LogErrorf("Failed to create script thread"); return NULL; } *source_line_offset = ctx.output.line_offset; @@ -81,7 +80,7 @@ int RunScriptSourceThread(char **result, V8Engine *e, const char *source, bool btn = CreateScriptThread(&ctx); if (btn == false) { - return CodeExecuteErr; + return NVM_UNEXPECTED_ERR; } *result = ctx.output.result; @@ -140,12 +139,13 @@ bool CreateScriptThread(v8ThreadContext *ctx) { return false; } + int timeout = ctx->e->timeout; bool is_kill = false; //thread safe while(1) { if (ctx->is_finished == true) { if (is_kill == true) { - ctx->output.ret = CodeTimeOut; + ctx->output.ret = NVM_EXE_TIMEOUT_ERR; } break; } else { @@ -156,11 +156,12 @@ bool CreateScriptThread(v8ThreadContext *ctx) { continue; } int diff = MicroSecondDiff(tcEnd, tcBegin); - if (diff >= KillTimeMicros && is_kill == false) { + + if (diff >= timeout && is_kill == false) { + LogErrorf("CreateScriptThread timeout timeout:%d diff:%d\n", timeout, diff); TerminateExecution(ctx->e); is_kill = true; } - } } return true; diff --git a/rpc/gateway.go b/rpc/gateway.go index b5a043604..14b2e196c 100644 --- a/rpc/gateway.go +++ b/rpc/gateway.go @@ -25,7 +25,7 @@ const ( const ( // DefaultHTTPLimit default max http conns DefaultHTTPLimit = 128 - // MaxRecvMsgSize Deafult max message size gateway's grpc client can receive + // MaxGateWayRecvMsgSize Deafult max message size gateway's grpc client can receive MaxGateWayRecvMsgSize = 64 * 1024 * 1024 ) diff --git a/util/uint128_test.go b/util/uint128_test.go index 16e906f52..1f09606d0 100644 --- a/util/uint128_test.go +++ b/util/uint128_test.go @@ -14,6 +14,24 @@ const ( maxUint64 = ^uint64(0) ) +func TestMax(t *testing.T) { + bigMaxUint64 := &big.Int{} + bigMaxUint64.SetString(strings.Repeat("f", 16), 16) + fmt.Println(strings.Repeat("f", 16), bigMaxUint64.String()) + + bigMaxUint128 := &big.Int{} + bigMaxUint128.SetString(strings.Repeat("f", 32), 16) + fmt.Println(strings.Repeat("f", 32), bigMaxUint128.String()) + + bigMaxUint256 := &big.Int{} + bigMaxUint256.SetString(strings.Repeat("f", 64), 16) + fmt.Println(strings.Repeat("f", 64), bigMaxUint256.String()) + + bigMaxUint512 := &big.Int{} + bigMaxUint512.SetString(strings.Repeat("f", 128), 16) + fmt.Println(strings.Repeat("f", 128), bigMaxUint512.String()) +} + func TestUint128(t *testing.T) { bigInt0 := big.NewInt(0)