diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 90ee860c1..000000000 --- a/Dockerfile +++ /dev/null @@ -1,43 +0,0 @@ -# Stage 1 of 2 stage [build] -FROM golang:stretch - -ENV GOPATH /go -ENV PATH $GOPATH/bin:/usr/local/go/bin:$PATH - -RUN mkdir -p "$GOPATH/src" "$GOPATH/bin" && chmod -R 777 "$GOPATH" - -WORKDIR /go/src/github.com/nebulasio/go-nebulas - -RUN apt-get update && \ - apt-get -yy -q install git build-essential protobuf-compiler sudo - -# Build Nebulas -COPY . /go/src/github.com/nebulasio/go-nebulas - -RUN go get -u github.com/golang/dep/cmd/dep && \ - go get -u golang.org/x/tools/cmd/goimports && \ - make dep && \ - make deploy-v8 && \ - make build - -# Stage 2 of 2 [copy assets to thin image] -FROM debian:stretch - -WORKDIR /nebulas - -ENV PATH /usr/local/bin:$PATH - -# Copy v8 libs from builder -COPY --from=0 /usr/local/lib /usr/local/lib -RUN ldconfig - -# Copy neb and crash reporter from builder -COPY --from=0 /go/src/github.com/nebulasio/go-nebulas/neb /usr/local/bin/neb -COPY --from=0 /go/src/github.com/nebulasio/go-nebulas/nebulas_crashreporter /usr/local/bin/nebulas_crashreporter - -# Copy conf & keydir to enable bootstrapping -COPY --from=0 /go/src/github.com/nebulasio/go-nebulas/keydir /nebulas/keydir -COPY --from=0 /go/src/github.com/nebulasio/go-nebulas/conf /nebulas/conf - -ENTRYPOINT ["/usr/local/bin/neb"] - diff --git a/Gopkg.lock b/Gopkg.lock index 10d98d44f..f85651ca2 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -10,11 +10,7 @@ [[projects]] branch = "master" name = "github.com/agl/ed25519" - packages = [ - ".", - "edwards25519", - "extra25519" - ] + packages = [".","edwards25519","extra25519"] revision = "5312a61534124124185d41f09206b9fef1d88403" [[projects]] @@ -44,10 +40,7 @@ [[projects]] branch = "master" name = "github.com/docker/spdystream" - packages = [ - ".", - "spdy" - ] + packages = [".","spdy"] revision = "bc6354cbbc295e925e4c611ffe90c1f287ee54db" [[projects]] @@ -58,10 +51,7 @@ [[projects]] name = "github.com/gogo/protobuf" - packages = [ - "io", - "proto" - ] + packages = ["io","proto"] revision = "100ba4e885062801d56799d78530b73b178a78f3" version = "v0.4" @@ -74,16 +64,7 @@ [[projects]] branch = "master" name = "github.com/golang/protobuf" - packages = [ - "jsonpb", - "proto", - "protoc-gen-go/descriptor", - "ptypes", - "ptypes/any", - "ptypes/duration", - "ptypes/struct", - "ptypes/timestamp" - ] + packages = ["jsonpb","proto","protoc-gen-go/descriptor","ptypes","ptypes/any","ptypes/duration","ptypes/struct","ptypes/timestamp"] revision = "ae59567b9aab61b50b2590679a62c3c044030b61" [[projects]] @@ -92,6 +73,12 @@ packages = ["."] revision = "553a641470496b2327abcac10b36396bd98e45c9" +[[projects]] + branch = "master" + name = "github.com/google/keytransparency" + packages = ["core/crypto/vrf"] + revision = "e5573858922e6a9456ab5557779b37d30af56c4c" + [[projects]] name = "github.com/gorilla/websocket" packages = ["."] @@ -131,33 +118,18 @@ [[projects]] branch = "master" name = "github.com/hashicorp/golang-lru" - packages = [ - ".", - "simplelru" - ] + packages = [".","simplelru"] revision = "0a025b7e63adc15a622f29b0b2c4c3848243bbf6" [[projects]] branch = "master" name = "github.com/huin/goupnp" - packages = [ - ".", - "dcps/internetgateway1", - "dcps/internetgateway2", - "httpu", - "scpd", - "soap", - "ssdp" - ] + packages = [".","dcps/internetgateway1","dcps/internetgateway2","httpu","scpd","soap","ssdp"] revision = "5b7801abd885f2f7458add715aee47e7df5370a0" [[projects]] name = "github.com/influxdata/influxdb" - packages = [ - "client", - "models", - "pkg/escape" - ] + packages = ["client","models","pkg/escape"] revision = "6d2685d1738277a1c2672fc58df7994627769be6" version = "v1.4.2" @@ -200,12 +172,7 @@ [[projects]] branch = "master" name = "github.com/jbenet/goprocess" - packages = [ - ".", - "context", - "periodic", - "ratelimit" - ] + packages = [".","context","periodic","ratelimit"] revision = "b497e2f366b8624394fb2e89c10ab607bebdde0b" [[projects]] @@ -229,20 +196,13 @@ [[projects]] branch = "master" name = "github.com/libp2p/go-libp2p" - packages = [ - "p2p/host/basic", - "p2p/protocol/identify", - "p2p/protocol/identify/pb" - ] + packages = ["p2p/host/basic","p2p/protocol/identify","p2p/protocol/identify/pb"] revision = "edb6434ddf456f58fbe2538d5336435a23915bd9" [[projects]] branch = "master" name = "github.com/libp2p/go-libp2p-circuit" - packages = [ - ".", - "pb" - ] + packages = [".","pb"] revision = "4730183d80def7bc574e1b486b1f6e97a5c67bb9" [[projects]] @@ -253,10 +213,7 @@ [[projects]] name = "github.com/libp2p/go-libp2p-crypto" - packages = [ - ".", - "pb" - ] + packages = [".","pb"] revision = "e89e1de117dd65c6129d99d1d853f48bc847cf17" [[projects]] @@ -284,10 +241,7 @@ [[projects]] branch = "master" name = "github.com/libp2p/go-libp2p-kbucket" - packages = [ - ".", - "keyspace" - ] + packages = [".","keyspace"] revision = "5ce3bb926eeeadf90708fec12343cbf858ed24b6" [[projects]] @@ -299,11 +253,7 @@ [[projects]] branch = "master" name = "github.com/libp2p/go-libp2p-metrics" - packages = [ - ".", - "conn", - "stream" - ] + packages = [".","conn","stream"] revision = "1ac87496e992290629575655bea4330dd4ab211e" [[projects]] @@ -324,10 +274,7 @@ [[projects]] name = "github.com/libp2p/go-libp2p-peerstore" - packages = [ - ".", - "addr" - ] + packages = [".","addr"] revision = "89838b9577a2b83eb3e1632c162cfd0daeee58e3" [[projects]] @@ -339,10 +286,7 @@ [[projects]] branch = "master" name = "github.com/libp2p/go-libp2p-secio" - packages = [ - ".", - "pb" - ] + packages = [".","pb"] revision = "0cdc8339ad2e4f7f32e67b6a481bfbd48be5c704" [[projects]] @@ -365,10 +309,7 @@ [[projects]] branch = "master" name = "github.com/libp2p/go-msgio" - packages = [ - ".", - "mpool" - ] + packages = [".","mpool"] revision = "d82125c9907e1365775356505f14277d47dfd4d6" [[projects]] @@ -380,11 +321,7 @@ [[projects]] branch = "master" name = "github.com/libp2p/go-reuseport" - packages = [ - ".", - "poll", - "singlepoll" - ] + packages = [".","poll","singlepoll"] revision = "2a863ed0ff1167e16ff48dd1de862b180af3bf27" [[projects]] @@ -449,10 +386,7 @@ [[projects]] branch = "master" name = "github.com/multiformats/go-multicodec" - packages = [ - ".", - "json" - ] + packages = [".","json"] revision = "fd289ac41fba9e574f214ab2a808a1678ef8810c" [[projects]] @@ -476,11 +410,7 @@ [[projects]] branch = "master" name = "github.com/nebulasio/grpc-gateway" - packages = [ - "runtime", - "runtime/internal", - "utilities" - ] + packages = ["runtime","runtime/internal","utilities"] revision = "9ed0e3fde466911904aef99bf3ecc8197d54b21d" [[projects]] @@ -504,10 +434,7 @@ [[projects]] branch = "master" name = "github.com/rcrowley/go-metrics" - packages = [ - ".", - "exp" - ] + packages = [".","exp"] revision = "1f30fe9094a513ce4c700b9a54458bbb0c96996c" [[projects]] @@ -519,15 +446,7 @@ [[projects]] branch = "master" name = "github.com/robertkrimen/otto" - packages = [ - ".", - "ast", - "dbg", - "file", - "parser", - "registry", - "token" - ] + packages = [".","ast","dbg","file","parser","registry","token"] revision = "68a29f5e29b18d7367e686c43cf74eba1900f548" [[projects]] @@ -557,29 +476,13 @@ [[projects]] branch = "master" name = "github.com/stretchr/testify" - packages = [ - "assert", - "require" - ] + packages = ["assert","require"] revision = "890a5c3458b43e6104ff5da8dfa139d013d77544" [[projects]] branch = "master" name = "github.com/syndtr/goleveldb" - packages = [ - "leveldb", - "leveldb/cache", - "leveldb/comparer", - "leveldb/errors", - "leveldb/filter", - "leveldb/iterator", - "leveldb/journal", - "leveldb/memdb", - "leveldb/opt", - "leveldb/storage", - "leveldb/table", - "leveldb/util" - ] + packages = ["leveldb","leveldb/cache","leveldb/comparer","leveldb/errors","leveldb/filter","leveldb/iterator","leveldb/journal","leveldb/memdb","leveldb/opt","leveldb/storage","leveldb/table","leveldb/util"] revision = "b89cc31ef7977104127d34c1bd31ebd1a9db2199" [[projects]] @@ -594,12 +497,6 @@ revision = "cfb38830724cc34fedffe9a2a29fb54fa9169cd1" version = "v1.20.0" -[[projects]] - branch = "master" - name = "github.com/vrischmann/go-metrics-influxdb" - packages = ["."] - revision = "43af8332c303f62ef62663b02b3b7d8a9802002a" - [[projects]] branch = "master" name = "github.com/whyrusleeping/go-logging" @@ -669,129 +566,48 @@ [[projects]] branch = "master" name = "golang.org/x/crypto" - packages = [ - "blake2s", - "blowfish", - "pbkdf2", - "ripemd160", - "scrypt", - "sha3", - "ssh/terminal" - ] + packages = ["blake2s","blowfish","pbkdf2","ripemd160","scrypt","sha3","ssh/terminal"] revision = "faadfbdc035307d901e69eea569f5dda451a3ee3" [[projects]] branch = "master" name = "golang.org/x/net" - packages = [ - "context", - "html", - "html/atom", - "html/charset", - "http2", - "http2/hpack", - "idna", - "internal/timeseries", - "lex/httplex", - "netutil", - "trace" - ] + packages = ["context","html","html/atom","html/charset","http2","http2/hpack","idna","internal/timeseries","lex/httplex","netutil","trace"] revision = "8351a756f30f1297fe94bbf4b767ec589c6ea6d0" [[projects]] branch = "master" name = "golang.org/x/sys" - packages = [ - "unix", - "windows" - ] + packages = ["unix","windows"] revision = "062cd7e4e68206d8bab9b18396626e855c992658" [[projects]] branch = "master" name = "golang.org/x/text" - packages = [ - "collate", - "collate/build", - "encoding", - "encoding/charmap", - "encoding/htmlindex", - "encoding/internal", - "encoding/internal/identifier", - "encoding/japanese", - "encoding/korean", - "encoding/simplifiedchinese", - "encoding/traditionalchinese", - "encoding/unicode", - "internal/colltab", - "internal/gen", - "internal/tag", - "internal/triegen", - "internal/ucd", - "internal/utf8internal", - "language", - "runes", - "secure/bidirule", - "transform", - "unicode/bidi", - "unicode/cldr", - "unicode/norm", - "unicode/rangetable" - ] + packages = ["collate","collate/build","encoding","encoding/charmap","encoding/htmlindex","encoding/internal","encoding/internal/identifier","encoding/japanese","encoding/korean","encoding/simplifiedchinese","encoding/traditionalchinese","encoding/unicode","internal/colltab","internal/gen","internal/tag","internal/triegen","internal/ucd","internal/utf8internal","language","runes","secure/bidirule","transform","unicode/bidi","unicode/cldr","unicode/norm","unicode/rangetable"] revision = "1cbadb444a806fd9430d14ad08967ed91da4fa0a" [[projects]] branch = "master" name = "google.golang.org/genproto" - packages = [ - "googleapis/api/annotations", - "googleapis/rpc/status" - ] + packages = ["googleapis/api/annotations","googleapis/rpc/status"] revision = "f676e0f3ac6395ff1a529ae59a6670878a8371a6" [[projects]] name = "google.golang.org/grpc" - packages = [ - ".", - "balancer", - "balancer/base", - "balancer/roundrobin", - "codes", - "connectivity", - "credentials", - "encoding", - "grpclb/grpc_lb_v1/messages", - "grpclog", - "internal", - "keepalive", - "metadata", - "naming", - "peer", - "reflection", - "reflection/grpc_reflection_v1alpha", - "resolver", - "resolver/dns", - "resolver/passthrough", - "stats", - "status", - "tap", - "transport" - ] + packages = [".","balancer","balancer/base","balancer/roundrobin","codes","connectivity","credentials","encoding","grpclb/grpc_lb_v1/messages","grpclog","internal","keepalive","metadata","naming","peer","reflection","reflection/grpc_reflection_v1alpha","resolver","resolver/dns","resolver/passthrough","stats","status","tap","transport"] revision = "6b51017f791ae1cfbec89c52efdf444b13b550ef" version = "v1.9.2" [[projects]] name = "gopkg.in/sourcemap.v1" - packages = [ - ".", - "base64vlq" - ] + packages = [".","base64vlq"] revision = "6e83acea0053641eff084973fee085f0c193c61a" version = "v1.0.5" [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "3cbb9060c41566687af55087832faa959bbf5e39cb4349bb1de287051e43b585" + inputs-digest = "105cf7ff893a2ebe6f3323ec78756a46b9bc5f7dc0708b4ff00a04bef0f8faaf" solver-name = "gps-cdcl" solver-version = 1 diff --git a/account/manager.go b/account/manager.go index f33114b8b..d61d8daa5 100644 --- a/account/manager.go +++ b/account/manager.go @@ -21,6 +21,9 @@ package account import ( "errors" + "github.com/nebulasio/go-nebulas/crypto/hash" + "github.com/nebulasio/go-nebulas/crypto/keystore/secp256k1/vrf/secp256k1VRF" + "github.com/nebulasio/go-nebulas/util/byteutils" "path/filepath" @@ -386,6 +389,41 @@ func (m *Manager) SignBlock(addr *core.Address, block *core.Block) error { return block.Sign(signature) } +// GenerateRandomSeed generate rand +func (m *Manager) GenerateRandomSeed(addr *core.Address, args ...[]byte) (vrfSeed, vrfProof []byte, err error) { + + key, err := m.ks.GetUnlocked(addr.String()) + if err != nil { + logging.VLog().WithFields(logrus.Fields{ + "err": err, + }).Error("Failed to get unlocked private key to generate block rand.") + return nil, nil, ErrAccountIsLocked + } + + _, err = crypto.NewSignature(m.signatureAlg) + if err != nil { + return nil, nil, err + } + + seckey, err := key.(keystore.PrivateKey).Encoded() + if err != nil { + return nil, nil, err + } + + signer, err := secp256k1VRF.NewVRFSignerFromRawKey(seckey) + if err != nil { + return nil, nil, err + } + + data := hash.Sha3256(args...) + + seed, proof := signer.Evaluate(data) + if proof == nil { + return nil, nil, secp256k1VRF.ErrEvaluateFailed + } + return seed[:], proof, nil +} + // SignTransactionWithPassphrase sign transaction with the from passphrase func (m *Manager) SignTransactionWithPassphrase(addr *core.Address, tx *core.Transaction, passphrase []byte) error { // check sign addr is tx's from addr diff --git a/cmd/neb/main.go b/cmd/neb/main.go index f19505f2d..0c65227f6 100644 --- a/cmd/neb/main.go +++ b/cmd/neb/main.go @@ -27,6 +27,7 @@ import ( "syscall" "time" + "github.com/nebulasio/go-nebulas/core" "github.com/nebulasio/go-nebulas/neblet" "github.com/nebulasio/go-nebulas/util/logging" "github.com/urfave/cli" @@ -84,6 +85,8 @@ func neb(ctx *cli.Context) error { logging.Init(n.Config().App.LogFile, n.Config().App.LogLevel, n.Config().App.LogAge) + core.SetCompatibilityOptions(n.Config().Chain.ChainId) + // enable crash report if open the switch and configure the url if n.Config().App.EnableCrashReport && len(n.Config().App.CrashReportUrl) > 0 { InitCrashReporter(n.Config().App) diff --git a/cmd/network/main.go b/cmd/network/main.go index 29a1a9205..6ceb041bf 100644 --- a/cmd/network/main.go +++ b/cmd/network/main.go @@ -29,6 +29,7 @@ import ( "time" "github.com/libp2p/go-libp2p-interface-conn" + "github.com/nebulasio/go-nebulas/core" "github.com/nebulasio/go-nebulas/metrics" "github.com/nebulasio/go-nebulas/neblet" "github.com/nebulasio/go-nebulas/net" @@ -108,6 +109,8 @@ func run(mode, configPath string, packageSize, concurrentMessageCount, totalMess // init log. logging.Init(config.App.LogFile, config.App.LogLevel, config.App.LogAge) + core.SetCompatibilityOptions(config.Chain.ChainId) + // neblet. neblet, _ := neblet.New(config) netService, err := net.NewNebService(neblet) diff --git a/conf/default/config.conf b/conf/default/config.conf index 8deece1d3..15b310cee 100644 --- a/conf/default/config.conf +++ b/conf/default/config.conf @@ -21,7 +21,7 @@ rpc { http_listen: ["0.0.0.0:8685"] http_module: ["api","admin"] # HTTP CORS allowed origins - # http_cors: [] + http_cors: ["*"] } app { @@ -29,7 +29,7 @@ app { log_file: "logs" enable_crash_report: true crash_report_url: "https://crashreport.nebulas.io" - pprof:{ + pprof:{ http_listen: "0.0.0.0:8888" } } diff --git a/consensus/dpos/dpos.go b/consensus/dpos/dpos.go index b65779d84..fb0ccd7d3 100644 --- a/consensus/dpos/dpos.go +++ b/consensus/dpos/dpos.go @@ -56,6 +56,7 @@ var ( ErrGenerateNextConsensusState = errors.New("Failed to generate next consensus state") ErrDoubleBlockMinted = errors.New("double block minted") ErrAppendNewBlockFailed = errors.New("failed to append new block to real chain") + ErrInvalidArgument = errors.New("invalid argument") ) // Metrics @@ -375,33 +376,72 @@ func (dpos *Dpos) VerifyBlock(block *core.Block) error { if err := verifyBlockSign(miner, block); err != nil { return err } + + // check block random + if block.Height() >= core.RandomAvailableHeight && !block.HasRandomSeed() { + logging.VLog().WithFields(logrus.Fields{ + "blockHeight": block.Height(), + "compatibleHeight": core.RandomAvailableHeight, + }).Debug("No random found in block header.") + return core.ErrInvalidBlockRandom + } + dpos.slot.Add(block.Timestamp(), block) return nil } -func (dpos *Dpos) signBlock(block *core.Block) error { +func (dpos *Dpos) generateRandomSeed(block *core.Block, adminService rpcpb.AdminServiceClient) error { if dpos.enableRemoteSignServer == true { - conn, err := rpc.Dial(dpos.remoteSignServer) - if err != nil { - return err + if adminService == nil { + return ErrInvalidArgument } - adminService := rpcpb.NewAdminServiceClient(conn) - alg := keystore.SECP256K1 - resp, err := adminService.SignHash( + // generate VRF hash,proof + random, err := adminService.GenerateRandomSeed( context.Background(), - &rpcpb.SignHashRequest{ - Address: dpos.miner.String(), - Hash: block.Hash(), - Alg: uint32(alg), + &rpcpb.GenerateRandomSeedRequest{ + Address: dpos.miner.String(), + ParentHash: block.ParentHash(), + Height: block.Height(), }) - conn.Close() if err != nil { return err } - block.SetSignature(alg, resp.Data) + block.SetRandomSeed(random.VrfSeed, random.VrfProof) return nil } - return dpos.am.SignBlock(dpos.miner, block) + + // generate VRF hash,proof + inputs, err := dpos.chain.GetInputForVRFSigner(block.ParentHash(), block.Height()) + if err != nil { + return err + } + vrfSeed, vrfProof, err := dpos.am.GenerateRandomSeed(dpos.miner, inputs...) + if err != nil { + return err + } + block.SetRandomSeed(vrfSeed, vrfProof) + + return nil +} + +func (dpos *Dpos) remoteSignBlock(block *core.Block, adminService rpcpb.AdminServiceClient) error { + if adminService == nil { + return ErrInvalidArgument + } + alg := keystore.SECP256K1 + resp, err := adminService.SignHash( + context.Background(), + &rpcpb.SignHashRequest{ + Address: dpos.miner.String(), + Hash: block.Hash(), + Alg: uint32(alg), + }) + if err != nil { + return err + } + + block.SetSignature(alg, resp.Data) + return nil } func (dpos *Dpos) unlock(passphrase string) error { @@ -425,6 +465,24 @@ func (dpos *Dpos) newBlock(tail *core.Block, consensusState state.ConsensusState return nil, err } + var adminService rpcpb.AdminServiceClient + if dpos.enableRemoteSignServer == true { + conn, err := rpc.Dial(dpos.remoteSignServer) + defer func() { + if conn != nil { + conn.Close() + } + }() + if err != nil { + return nil, err + } + adminService = rpcpb.NewAdminServiceClient(conn) + } + + if block.Height() >= core.RandomAvailableHeight { + dpos.generateRandomSeed(block, adminService) + } + block.WorldState().SetConsensusState(consensusState) block.SetTimestamp(consensusState.TimeStamp()) block.CollectTransactions(deadlineInMs) @@ -436,7 +494,13 @@ func (dpos *Dpos) newBlock(tail *core.Block, consensusState state.ConsensusState go block.ReturnTransactions() return nil, err } - if err = dpos.signBlock(block); err != nil { + + if dpos.enableRemoteSignServer == true { + err = dpos.remoteSignBlock(block, adminService) + } else { + err = dpos.am.SignBlock(dpos.miner, block) + } + if err != nil { logging.CLog().WithFields(logrus.Fields{ "miner": dpos.miner, "block": block, @@ -648,3 +712,8 @@ func (dpos *Dpos) findProposer(now int64) (proposer byteutils.Hash, err error) { } return proposer, nil } + +// NumberOfBlocksInDynasty number of blocks in one dynasty +func (dpos *Dpos) NumberOfBlocksInDynasty() uint64 { + return uint64(DynastyIntervalInMs) / uint64(BlockIntervalInMs) +} diff --git a/core/address.go b/core/address.go index 37e9c926b..0e5535c49 100644 --- a/core/address.go +++ b/core/address.go @@ -27,6 +27,9 @@ import ( // AddressType address type type AddressType byte +// UndefinedAddressType undefined +const UndefinedAddressType AddressType = 0x00 + // address type enum const ( AccountAddress AddressType = 0x57 + iota @@ -151,6 +154,9 @@ func (a *Address) Equals(b *Address) bool { // Type return the type of address. func (a *Address) Type() AddressType { + if len(a.address) <= AddressTypeIndex { + return UndefinedAddressType + } return AddressType(a.address[AddressTypeIndex]) } diff --git a/core/block.go b/core/block.go index b5dfcab3a..29f28e83a 100644 --- a/core/block.go +++ b/core/block.go @@ -74,6 +74,9 @@ type BlockHeader struct { // sign alg keystore.Algorithm sign byteutils.Hash + + // rand + random *corepb.Random } // ToProto converts domain BlockHeader to proto BlockHeader @@ -90,6 +93,7 @@ func (b *BlockHeader) ToProto() (proto.Message, error) { ChainId: b.chainID, Alg: uint32(b.alg), Sign: b.sign, + Random: b.random, }, nil } @@ -121,6 +125,7 @@ func (b *BlockHeader) FromProto(msg proto.Message) error { b.alg = alg b.sign = msg.Sign + b.random = msg.Random return nil } return ErrInvalidProtoToBlockHeader @@ -189,6 +194,13 @@ func (block *Block) FromProto(msg proto.Message) error { if err := block.header.FromProto(msg.Header); err != nil { return err } + if msg.Height >= RandomAvailableHeight && !block.HasRandomSeed() { + logging.VLog().WithFields(logrus.Fields{ + "blockHeight": msg.Height, + "compatibleHeight": RandomAvailableHeight, + }).Debug("No random found in block header.") + return ErrInvalidProtoToBlockHeader + } block.transactions = make(Transactions, len(msg.Transactions)) for idx, v := range msg.Transactions { if v != nil { @@ -227,6 +239,7 @@ func NewBlock(chainID uint32, coinbase *Address, parent *Block) (*Block, error) coinbase: coinbase, timestamp: time.Now().Unix(), consensusRoot: &consensuspb.ConsensusRoot{}, + random: &corepb.Random{}, }, transactions: make(Transactions, 0), dependency: dag.NewDag(), @@ -270,6 +283,19 @@ func (block *Block) Sign(signature keystore.Signature) error { return nil } +// SetRandomSeed set block.header.random +func (block *Block) SetRandomSeed(vrfseed, vrfproof []byte) { + block.header.random = &corepb.Random{ + VrfSeed: vrfseed, + VrfProof: vrfproof, + } +} + +// HasRandomSeed check random if exists +func (block *Block) HasRandomSeed() bool { + return block.header.random != nil && block.header.random.VrfSeed != nil && block.header.random.VrfProof != nil +} + // ChainID returns block's chainID func (block *Block) ChainID() uint32 { return block.header.chainID @@ -355,6 +381,24 @@ func (block *Block) Transactions() Transactions { return block.transactions } +// RandomSeed block random seed (VRF) +func (block *Block) RandomSeed() string { + if block.height >= RandomAvailableHeight { + return byteutils.Hex(block.header.random.VrfSeed) + } + return "" +} + +// RandomAvailable check if Math.random available in contract +func (block *Block) RandomAvailable() bool { + return block.height >= RandomAvailableHeight +} + +// DateAvailable check if date available in contract +func (block *Block) DateAvailable() bool { + return block.height >= DateAvailableHeight +} + // LinkParentBlock link parent block, return true if hash is the same; false otherwise. func (block *Block) LinkParentBlock(chain *BlockChain, parentBlock *Block) error { if !block.ParentHash().Equals(parentBlock.Hash()) { @@ -750,7 +794,16 @@ func (block *Block) Seal() error { } func (block *Block) String() string { - return fmt.Sprintf(`{"height": %d, "hash": "%s", "parent_hash": "%s", "acc_root": "%s", "timestamp": %d, "tx": %d, "miner": "%s"}`, + random := "" + if block.height >= RandomAvailableHeight && block.header.random != nil { + if block.header.random.VrfSeed != nil { + random += "/vrf_seed/" + byteutils.Hex(block.header.random.VrfSeed) + } + if block.header.random.VrfProof != nil { + random += "/vrf_proof/" + byteutils.Hex(block.header.random.VrfProof) + } + } + return fmt.Sprintf(`{"height": %d, "hash": "%s", "parent_hash": "%s", "acc_root": "%s", "timestamp": %d, "tx": %d, "miner": "%s", "random": "%s"}`, block.height, block.header.hash, block.header.parentHash, @@ -758,6 +811,7 @@ func (block *Block) String() string { block.header.timestamp, len(block.transactions), byteutils.Hash(block.header.consensusRoot.Proposer).Base58(), + random, ) } diff --git a/core/block_pool.go b/core/block_pool.go index 85737a7eb..0c47702bc 100644 --- a/core/block_pool.go +++ b/core/block_pool.go @@ -19,12 +19,16 @@ package core import ( + "bytes" "sync" "time" "github.com/gogo/protobuf/proto" lru "github.com/hashicorp/golang-lru" "github.com/nebulasio/go-nebulas/core/pb" + "github.com/nebulasio/go-nebulas/crypto" + "github.com/nebulasio/go-nebulas/crypto/hash" + "github.com/nebulasio/go-nebulas/crypto/keystore/secp256k1/vrf/secp256k1VRF" "github.com/nebulasio/go-nebulas/net" "github.com/nebulasio/go-nebulas/util/byteutils" @@ -451,7 +455,6 @@ func (pool *BlockPool) push(sender string, block *Block) error { cache.Remove(lb.hash.Hex()) return err } - // remove allBlocks from cache. for _, v := range allBlocks { cache.Remove(v.Hash().Hex()) @@ -461,6 +464,46 @@ func (pool *BlockPool) push(sender string, block *Block) error { return pool.bc.ConsensusHandler().ForkChoice() } +func vrfProof(block *Block, args [][]byte) error { + signature, err := crypto.NewSignature(block.Alg()) + if err != nil { + return err + } + pub, err := signature.RecoverPublic(block.Hash(), block.Signature()) + if err != nil { + return err + } + pubdata, err := pub.Encoded() + if err != nil { + return err + } + + verifier, err := secp256k1VRF.NewVRFVerifierFromRawKey(pubdata) + if err != nil { + logging.VLog().WithFields(logrus.Fields{ + "err": err, + }).Error("Failed to new VRF verifier.") + return err + } + + data := hash.Sha3256(args...) + index, err := verifier.ProofToHash(data, block.header.random.VrfProof) + if err != nil { + logging.VLog().WithFields(logrus.Fields{ + "err": err, + }).Error("Failed to calculate VRF proof.") + return err + } + + if !bytes.Equal(index[:], block.header.random.VrfSeed) { + logging.VLog().WithFields(logrus.Fields{ + "block": block, + }).Error("VRF proof failed.") + return ErrVRFProofFailed + } + return nil +} + func (pool *BlockPool) setBlockChain(bc *BlockChain) { pool.bc = bc } @@ -491,6 +534,60 @@ func (lb *linkedBlock) travelToLinkAndReturnAllValidBlocks(parentBlock *Block) ( return nil, nil, err } + // verify vrf + if lb.block.height >= RandomAvailableHeight { + // prepare vrf inputs + inputs := make([][]byte, 0) + + nob := lb.chain.ConsensusHandler().NumberOfBlocksInDynasty() + i := uint64(0) + tmp := lb + for i < nob*2 && tmp != nil { + i++ + tmp = tmp.parentBlock + } + if tmp == nil { + if lb.block.height > nob*2 { + b := lb.chain.GetBlockOnCanonicalChainByHeight(lb.block.height - nob*2) + if b == nil { + logging.VLog().WithFields(logrus.Fields{ + "blockHeight": lb.block.height, + "targetHeight": lb.block.height - nob, + "numOfBlocksInDynasty": nob, + }).Error("Block not found, unexpected error.") + metricsUnexpectedBehavior.Update(1) + return nil, nil, ErrNotBlockInCanonicalChain + } + inputs = append(inputs, b.Hash()) + } else { + inputs = append(inputs, lb.chain.GenesisBlock().Hash()) + } + } else { + inputs = append(inputs, tmp.block.Hash()) + } + + if parentBlock.height >= RandomAvailableHeight { + if !parentBlock.HasRandomSeed() { + logging.VLog().WithFields(logrus.Fields{ + "parent": parentBlock, + }).Error("Parent block has no random seed, unexpected error.") + metricsUnexpectedBehavior.Update(1) + return nil, nil, ErrInvalidBlockRandom + } + inputs = append(inputs, parentBlock.header.random.VrfSeed) + } else { + inputs = append(inputs, lb.chain.GenesisBlock().Hash()) + } + + if err := vrfProof(lb.block, inputs); err != nil { + logging.CLog().WithFields(logrus.Fields{ + "err": err, + "lb.block": lb.block, + }).Error("VRF proof failed.") + return nil, nil, ErrVRFProofFailed + } + } + if err := lb.block.VerifyExecution(); err != nil { logging.VLog().WithFields(logrus.Fields{ "block": lb.block, diff --git a/core/block_test.go b/core/block_test.go index f397ba9bd..0c822d61f 100644 --- a/core/block_test.go +++ b/core/block_test.go @@ -199,6 +199,9 @@ func (c *mockConsensus) NewState(root *consensuspb.ConsensusRoot, stor storage.S func (c *mockConsensus) GenesisConsensusState(*BlockChain, *corepb.Genesis) (state.ConsensusState, error) { return newMockConsensusState(0) } +func (c *mockConsensus) NumberOfBlocksInDynasty() uint64 { + return 210 +} type mockManager struct{} @@ -219,6 +222,9 @@ func (m mockManager) Update(*Address, []byte, []byte) error { return nil } func (m mockManager) Load([]byte, []byte) (*Address, error) { return nil, nil } func (m mockManager) Import([]byte, []byte) (*Address, error) { return nil, nil } func (m mockManager) Remove(*Address, []byte) error { return nil } +func (m mockManager) GenerateRandomSeed(addr *Address, args ...[]byte) (vrfSeed, vrfProof []byte, err error) { + return nil, nil, nil +} var ( received = []byte{} diff --git a/core/blockchain.go b/core/blockchain.go index a47d7b749..a0ecbe6ca 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -19,6 +19,8 @@ package core import ( + "crypto/rand" + "io" "strings" "time" @@ -76,8 +78,6 @@ type BlockChain struct { } const ( - // TestNetID chain id for test net. - TestNetID = 1 // EagleNebula chain id for 1.x EagleNebula = 1 << 4 @@ -470,6 +470,48 @@ func (bc *BlockChain) GetBlockOnCanonicalChainByHash(blockHash byteutils.Hash) * return blockByHeight } +// GetInputForVRFSigner returns [ getBlock(block.height - 2 * dynasty.size).hash, block.parent.seed ] +func (bc *BlockChain) GetInputForVRFSigner(parentHash byteutils.Hash, height uint64) (out [][]byte, err error) { + if parentHash == nil || height < RandomAvailableHeight { + return nil, ErrInvalidArgument + } + + nob := bc.consensusHandler.NumberOfBlocksInDynasty() + if height > nob*2 { + b := bc.GetBlockOnCanonicalChainByHeight(height - nob*2) + if b == nil { + logging.VLog().WithFields(logrus.Fields{ + "blockHeight": height, + "targetHeight": height - nob, + "numOfBlocksInDynasty": nob, + }).Error("Block not found.") + return nil, ErrNotBlockInCanonicalChain + } + out = append(out, b.Hash()) + } else { + out = append(out, bc.GenesisBlock().Hash()) + } + + parent := bc.GetBlockOnCanonicalChainByHash(parentHash) + if parent == nil || parent.height+1 != height { + return nil, ErrInvalidBlockHash + } + + if parent.height >= RandomAvailableHeight { + if !parent.HasRandomSeed() { + logging.VLog().WithFields(logrus.Fields{ + "parent": parent, + }).Error("Parent block has no random seed, unexpected error.") + metricsUnexpectedBehavior.Update(1) + return nil, ErrInvalidBlockRandom + } + out = append(out, parent.header.random.VrfSeed) + } else { + out = append(out, bc.GenesisBlock().Hash()) + } + return +} + // FindCommonAncestorWithTail return the block's common ancestor with current tail func (bc *BlockChain) FindCommonAncestorWithTail(block *Block) (*Block, error) { if block == nil { @@ -683,6 +725,13 @@ func (bc *BlockChain) SimulateTransactionExecution(tx *Transaction) (*SimulateRe if err != nil { return nil, err } + + sVrfSeed, sVrfProof := make([]byte, 32), make([]byte, 129) + _, _ = io.ReadFull(rand.Reader, sVrfSeed) + _, _ = io.ReadFull(rand.Reader, sVrfProof) + block.header.random.VrfSeed = sVrfSeed + block.header.random.VrfProof = sVrfProof + defer block.RollBack() // simulate execution. diff --git a/core/compatibility.go b/core/compatibility.go new file mode 100644 index 000000000..3d0006f6c --- /dev/null +++ b/core/compatibility.go @@ -0,0 +1,121 @@ +// 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 ( + "github.com/nebulasio/go-nebulas/util/logging" + "github.com/sirupsen/logrus" +) + +const ( + // MainNetID mainnet id + MainNetID uint32 = 1 + + // TestNetID testnet id + TestNetID uint32 = 1001 +) + +// mainnet/testnet +const ( + // DefaultTransferFromContractEventRecordableHeight + DefaultTransferFromContractEventRecordableHeight uint64 = 250000 + + // DefaultAcceptFuncAvailableHeight + DefaultAcceptFuncAvailableHeight uint64 = 250000 + + // DefaultRandomAvailableHeight + DefaultRandomAvailableHeight uint64 = 250000 + + // DefaultDateAvailableHeight + DefaultDateAvailableHeight uint64 = 250000 + + // DefaultRecordCallContractResultHeight + DefaultRecordCallContractResultHeight uint64 = 250000 +) + +// others, e.g. local/develop +const ( + // LocalTransferFromContractEventRecordableHeight + LocalTransferFromContractEventRecordableHeight uint64 = 0 + + // LocalAcceptFuncAvailableHeight + LocalAcceptFuncAvailableHeight uint64 = 0 + + // LocalRandomAvailableHeight + LocalRandomAvailableHeight uint64 = 0 + + // LocalDateAvailableHeight + LocalDateAvailableHeight uint64 = 0 + + // LocalRecordCallContractResultHeight + LocalRecordCallContractResultHeight uint64 = 0 +) + +var ( + // TransferFromContractEventRecordableHeight record event 'TransferFromContractEvent' since this height + TransferFromContractEventRecordableHeight = DefaultTransferFromContractEventRecordableHeight + + // AcceptFuncAvailableHeight 'accept' func available since this height + AcceptFuncAvailableHeight = DefaultAcceptFuncAvailableHeight + + // RandomAvailableHeight make 'Math.random' available in contract since this height + RandomAvailableHeight = DefaultRandomAvailableHeight + + // DateAvailableHeight make 'Date' available in contract since this height + DateAvailableHeight = DefaultDateAvailableHeight + + // RecordCallContractResultHeight record result of call contract to event `TopicTransactionExecutionResult` since this height + RecordCallContractResultHeight = DefaultRecordCallContractResultHeight +) + +// SetCompatibilityOptions set compatibility height according to chain_id +func SetCompatibilityOptions(chainID uint32) { + + if chainID == MainNetID || chainID == TestNetID { + logging.VLog().WithFields(logrus.Fields{ + "chain_id": chainID, + "TransferFromContractEventRecordableHeight": TransferFromContractEventRecordableHeight, + "AcceptFuncAvailableHeight": AcceptFuncAvailableHeight, + "RandomAvailableHeight": RandomAvailableHeight, + "DateAvailableHeight": DateAvailableHeight, + "RecordCallContractResultHeight": RecordCallContractResultHeight, + }).Info("Set compatibility options for mainnet/testnet.") + return + } + + TransferFromContractEventRecordableHeight = LocalTransferFromContractEventRecordableHeight + + AcceptFuncAvailableHeight = LocalAcceptFuncAvailableHeight + + RandomAvailableHeight = LocalRandomAvailableHeight + + DateAvailableHeight = LocalDateAvailableHeight + + RecordCallContractResultHeight = LocalRecordCallContractResultHeight + + logging.VLog().WithFields(logrus.Fields{ + "chain_id": chainID, + "TransferFromContractEventRecordableHeight": TransferFromContractEventRecordableHeight, + "AcceptFuncAvailableHeight": AcceptFuncAvailableHeight, + "RandomAvailableHeight": RandomAvailableHeight, + "DateAvailableHeight": DateAvailableHeight, + "RecordCallContractResultHeight": RecordCallContractResultHeight, + }).Info("Set compatibility options for local.") + +} diff --git a/core/event.go b/core/event.go index 744b3ec66..a533b77e5 100644 --- a/core/event.go +++ b/core/event.go @@ -46,6 +46,9 @@ const ( // TopicDropTransaction drop tx (1): smaller nonce (2) expire txLifeTime TopicDropTransaction = "chain.dropTransaction" + + // TopicTransferFromContract transfer from contract + TopicTransferFromContract = "chain.transferFromContract" ) // EventSubscriber subscriber object diff --git a/core/pb/block.pb.go b/core/pb/block.pb.go index 474e66ca1..f6d575257 100644 --- a/core/pb/block.pb.go +++ b/core/pb/block.pb.go @@ -16,6 +16,7 @@ It has these top-level messages: NetBlocks NetBlock DownloadBlock + Random */ package corepb @@ -224,6 +225,7 @@ type BlockHeader struct { TxsRoot []byte `protobuf:"bytes,10,opt,name=txs_root,json=txsRoot,proto3" json:"txs_root,omitempty"` EventsRoot []byte `protobuf:"bytes,11,opt,name=events_root,json=eventsRoot,proto3" json:"events_root,omitempty"` ConsensusRoot *consensuspb.ConsensusRoot `protobuf:"bytes,12,opt,name=consensus_root,json=consensusRoot" json:"consensus_root,omitempty"` + Random *Random `protobuf:"bytes,13,opt,name=random" json:"random,omitempty"` } func (m *BlockHeader) Reset() { *m = BlockHeader{} } @@ -308,6 +310,13 @@ func (m *BlockHeader) GetConsensusRoot() *consensuspb.ConsensusRoot { return nil } +func (m *BlockHeader) GetRandom() *Random { + if m != nil { + return m.Random + } + return nil +} + type Block struct { Header *BlockHeader `protobuf:"bytes,1,opt,name=header" json:"header,omitempty"` Transactions []*Transaction `protobuf:"bytes,2,rep,name=transactions" json:"transactions,omitempty"` @@ -436,6 +445,30 @@ func (m *DownloadBlock) GetSign() []byte { return nil } +type Random struct { + VrfSeed []byte `protobuf:"bytes,1,opt,name=vrf_seed,json=vrfSeed,proto3" json:"vrf_seed,omitempty"` + VrfProof []byte `protobuf:"bytes,2,opt,name=vrf_proof,json=vrfProof,proto3" json:"vrf_proof,omitempty"` +} + +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 (m *Random) GetVrfSeed() []byte { + if m != nil { + return m.VrfSeed + } + return nil +} + +func (m *Random) GetVrfProof() []byte { + if m != nil { + return m.VrfProof + } + return nil +} + func init() { proto.RegisterType((*Account)(nil), "corepb.Account") proto.RegisterType((*Data)(nil), "corepb.Data") @@ -445,54 +478,59 @@ func init() { proto.RegisterType((*NetBlocks)(nil), "corepb.NetBlocks") proto.RegisterType((*NetBlock)(nil), "corepb.NetBlock") proto.RegisterType((*DownloadBlock)(nil), "corepb.DownloadBlock") + proto.RegisterType((*Random)(nil), "corepb.Random") } func init() { proto.RegisterFile("block.proto", fileDescriptorBlock) } var fileDescriptorBlock = []byte{ - // 698 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x54, 0x6d, 0x8b, 0xd3, 0x40, - 0x10, 0x26, 0x6d, 0xfa, 0x36, 0x69, 0x8f, 0x63, 0x3d, 0x24, 0x56, 0xe5, 0x4a, 0x44, 0x28, 0x8a, - 0x2d, 0x9c, 0xc2, 0xf9, 0xf5, 0xf4, 0x3e, 0x9c, 0x22, 0x72, 0x04, 0xbf, 0x08, 0x42, 0x99, 0x6c, - 0xd6, 0x24, 0x98, 0xee, 0x86, 0xec, 0xf6, 0xbc, 0xfb, 0x19, 0xfe, 0x0f, 0xbf, 0xf8, 0x93, 0xfc, - 0x27, 0xb2, 0xb3, 0xe9, 0xdb, 0x79, 0x20, 0x7e, 0xea, 0x3e, 0xf3, 0x64, 0xa6, 0xf3, 0x3c, 0x33, - 0xbb, 0x10, 0x24, 0xa5, 0xe2, 0xdf, 0x66, 0x55, 0xad, 0x8c, 0x62, 0x5d, 0xae, 0x6a, 0x51, 0x25, - 0xe3, 0xd3, 0xac, 0x30, 0xf9, 0x2a, 0x99, 0x71, 0xb5, 0x9c, 0x4b, 0x91, 0xac, 0x4a, 0xd4, 0x85, - 0x9a, 0x67, 0xea, 0x45, 0x03, 0xe6, 0x5c, 0x2d, 0x97, 0x4a, 0xce, 0x53, 0xcc, 0xe6, 0x55, 0x62, - 0x7f, 0x5c, 0x81, 0xf1, 0xeb, 0x7f, 0x27, 0x4a, 0x2d, 0xa4, 0x5e, 0x69, 0x9b, 0xa7, 0x0d, 0x1a, - 0xe1, 0x32, 0xa3, 0x1f, 0x1e, 0xf4, 0xce, 0x38, 0x57, 0x2b, 0x69, 0x58, 0x08, 0x3d, 0x4c, 0xd3, - 0x5a, 0x68, 0x1d, 0x7a, 0x13, 0x6f, 0x3a, 0x8c, 0xd7, 0xd0, 0x32, 0x09, 0x96, 0x28, 0xb9, 0x08, - 0x5b, 0x8e, 0x69, 0x20, 0x3b, 0x82, 0x8e, 0x54, 0x36, 0xde, 0x9e, 0x78, 0x53, 0x3f, 0x76, 0x80, - 0x3d, 0x84, 0xc1, 0x15, 0xd6, 0x7a, 0x91, 0xa3, 0xce, 0x43, 0x9f, 0x32, 0xfa, 0x36, 0x70, 0x81, - 0x3a, 0x67, 0xc7, 0x10, 0x24, 0x45, 0x6d, 0xf2, 0x45, 0x55, 0x22, 0x17, 0x61, 0x87, 0x68, 0xa0, - 0xd0, 0xa5, 0x8d, 0x44, 0xaf, 0xc0, 0x3f, 0x47, 0x83, 0x8c, 0x81, 0x6f, 0x6e, 0x2a, 0x41, 0xcd, - 0x0c, 0x62, 0x3a, 0xdb, 0x4e, 0x2a, 0xbc, 0x29, 0x15, 0xa6, 0xeb, 0x4e, 0x1a, 0x18, 0xfd, 0x6c, - 0x41, 0xf0, 0xa9, 0x46, 0xa9, 0x91, 0x9b, 0x42, 0x49, 0x9b, 0x4d, 0x7f, 0xef, 0xa4, 0xd0, 0xd9, - 0xc6, 0xbe, 0xd6, 0x6a, 0xd9, 0xa4, 0xd2, 0x99, 0x1d, 0x40, 0xcb, 0x28, 0x6a, 0x7f, 0x18, 0xb7, - 0x8c, 0xb2, 0x8a, 0xae, 0xb0, 0x5c, 0x89, 0xa6, 0x6f, 0x07, 0xb6, 0x3a, 0x3b, 0xbb, 0x3a, 0x1f, - 0xc1, 0xc0, 0x14, 0x4b, 0xa1, 0x0d, 0x2e, 0xab, 0xb0, 0x3b, 0xf1, 0xa6, 0xed, 0x78, 0x1b, 0x60, - 0x13, 0xf0, 0x53, 0x34, 0x18, 0xf6, 0x26, 0xde, 0x34, 0x38, 0x19, 0xce, 0xdc, 0x94, 0x67, 0x56, - 0x5b, 0x4c, 0x0c, 0x7b, 0x00, 0x7d, 0x9e, 0x63, 0x21, 0x17, 0x45, 0x1a, 0xf6, 0x27, 0xde, 0x74, - 0x14, 0xf7, 0x08, 0xbf, 0x4b, 0xad, 0x85, 0x19, 0xea, 0x45, 0x55, 0x17, 0x5c, 0x84, 0x03, 0x67, - 0x61, 0x86, 0xfa, 0xd2, 0xe2, 0x35, 0x59, 0x16, 0xcb, 0xc2, 0x84, 0xb0, 0x21, 0x3f, 0x58, 0xcc, - 0x0e, 0xa1, 0x8d, 0x65, 0x16, 0x06, 0x54, 0xcf, 0x1e, 0xad, 0x6c, 0x5d, 0x64, 0x32, 0x1c, 0x3a, - 0xd9, 0xf6, 0x1c, 0xfd, 0x6e, 0x41, 0xf0, 0xc6, 0xee, 0xe0, 0x85, 0xc0, 0x54, 0xd4, 0x77, 0xda, - 0x75, 0x0c, 0x41, 0x85, 0xb5, 0x90, 0xc6, 0x0d, 0xd2, 0xb9, 0x06, 0x2e, 0x44, 0xa3, 0x1c, 0x43, - 0x9f, 0xab, 0x42, 0x26, 0xa8, 0xd7, 0x76, 0x6d, 0xf0, 0xbe, 0x37, 0x9d, 0xdb, 0xde, 0xec, 0x2a, - 0xef, 0xee, 0x2b, 0x6f, 0xfa, 0xef, 0xfd, 0xdd, 0x7f, 0x7f, 0xdb, 0x3f, 0x7b, 0x0c, 0x40, 0x7b, - 0xbc, 0xa8, 0x95, 0x32, 0x8d, 0x41, 0x03, 0x8a, 0xc4, 0x4a, 0x19, 0x5b, 0xdf, 0x5c, 0x6b, 0x47, - 0x3a, 0x83, 0x7a, 0xe6, 0x5a, 0x13, 0x75, 0x0c, 0x81, 0xb8, 0x12, 0xd2, 0x34, 0x6c, 0xe0, 0x54, - 0xb9, 0x10, 0x7d, 0x70, 0x06, 0x07, 0x9b, 0xfb, 0xe2, 0xbe, 0x19, 0xd2, 0x04, 0xc7, 0xb3, 0x4d, - 0xb8, 0x4a, 0x66, 0x6f, 0xd7, 0x67, 0x9b, 0x13, 0x8f, 0xf8, 0x2e, 0x7c, 0xef, 0xf7, 0xdb, 0x87, - 0x7e, 0xf4, 0xcb, 0x83, 0x0e, 0x79, 0xcc, 0x9e, 0x43, 0x37, 0x27, 0x9f, 0xc9, 0xdf, 0xe0, 0xe4, - 0xde, 0x7a, 0x19, 0x76, 0x46, 0x10, 0x37, 0x9f, 0xb0, 0x53, 0x18, 0x9a, 0xed, 0x22, 0xeb, 0xb0, - 0x35, 0x69, 0xef, 0xa6, 0xec, 0x2c, 0x79, 0xbc, 0xf7, 0x21, 0x7b, 0x06, 0x90, 0x8a, 0x4a, 0xc8, - 0x54, 0x48, 0x7e, 0x43, 0x2b, 0x1d, 0x9c, 0xc0, 0x2c, 0xc5, 0x8c, 0xb6, 0x2e, 0x8b, 0x77, 0x58, - 0x76, 0xdf, 0x76, 0x54, 0x64, 0xb9, 0xa1, 0xc1, 0xf9, 0x71, 0x83, 0xa2, 0x2f, 0x30, 0xf8, 0x28, - 0x0c, 0xb5, 0xa5, 0x37, 0xf7, 0xa5, 0xb9, 0x81, 0x74, 0x5f, 0x8e, 0xa0, 0x93, 0xa0, 0xe1, 0x6e, - 0x1d, 0xfc, 0xd8, 0x01, 0xf6, 0x14, 0xba, 0xf4, 0xa2, 0xe9, 0xb0, 0x4d, 0xdd, 0x8e, 0xf6, 0x04, - 0xc6, 0x0d, 0x19, 0x7d, 0x86, 0xfe, 0xba, 0xfa, 0x7f, 0x14, 0x7f, 0x02, 0x1d, 0xca, 0x6f, 0x24, - 0xdd, 0xaa, 0xed, 0xb8, 0xe8, 0x14, 0x46, 0xe7, 0xea, 0xbb, 0xb4, 0x6f, 0xc1, 0xa6, 0xfe, 0x5d, - 0x0f, 0x00, 0x6d, 0x52, 0x6b, 0xbb, 0x49, 0x49, 0x97, 0x5e, 0xc2, 0x97, 0x7f, 0x02, 0x00, 0x00, - 0xff, 0xff, 0xa7, 0xc4, 0x6c, 0xca, 0x93, 0x05, 0x00, 0x00, + // 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, } diff --git a/core/pb/block.proto b/core/pb/block.proto index 24e67e288..9e1f0f714 100644 --- a/core/pb/block.proto +++ b/core/pb/block.proto @@ -64,6 +64,7 @@ message BlockHeader { bytes txs_root = 10; bytes events_root = 11; consensuspb.ConsensusRoot consensus_root = 12; + Random random = 13; } message Block { @@ -90,3 +91,8 @@ message DownloadBlock { bytes hash = 1; bytes sign = 2; } + +message Random { + bytes vrf_seed = 1; + bytes vrf_proof = 2; +} \ No newline at end of file diff --git a/core/state/account_state.go b/core/state/account_state.go index 3ae8ccc8e..fed37295b 100644 --- a/core/state/account_state.go +++ b/core/state/account_state.go @@ -32,8 +32,9 @@ import ( // Errors var ( - ErrBalanceInsufficient = errors.New("cannot subtract a value which is bigger than current balance") - ErrAccountNotFound = errors.New("cannot found account in storage") + ErrBalanceInsufficient = errors.New("cannot subtract a value which is bigger than current balance") + ErrAccountNotFound = errors.New("cannot found account in storage") + ErrContractAccountNotFound = errors.New("cannot found contract account in storage please check contract address is valid or deploy is success") ) // account info in state Trie @@ -301,6 +302,10 @@ func (as *accountState) GetOrCreateUserAccount(addr byteutils.Hash) (Account, er // GetContractAccount from current AccountState func (as *accountState) GetContractAccount(addr byteutils.Hash) (Account, error) { acc, err := as.getAccount(addr) + + if err == ErrAccountNotFound { + err = ErrContractAccountNotFound + } if err != nil { return nil, err } diff --git a/core/transaction.go b/core/transaction.go index 673984229..7ac6ed326 100644 --- a/core/transaction.go +++ b/core/transaction.go @@ -67,6 +67,9 @@ var ( // MaxEventErrLength Max error length in event MaxEventErrLength = 256 + + // MaxResultLength max execution result length + MaxResultLength = 256 ) // TransactionEvent transaction event @@ -77,6 +80,15 @@ type TransactionEvent struct { Error string `json:"error"` } +// TransactionEventV2 add execution result +type TransactionEventV2 struct { + Hash string `json:"hash"` + Status int8 `json:"status"` + GasUsed string `json:"gas_used"` + Error string `json:"error"` + ExecuteResult string `json:"execute_result"` +} + // Transaction type is used to handle all transaction data. type Transaction struct { hash byteutils.Hash @@ -348,7 +360,8 @@ func (tx *Transaction) LoadPayload() (TxPayload, error) { return payload, err } -func submitTx(tx *Transaction, block *Block, ws WorldState, gas *util.Uint128, exeErr error, exeErrTy string) (bool, error) { +func submitTx(tx *Transaction, block *Block, ws WorldState, + gas *util.Uint128, exeErr error, exeErrTy string, exeResult string) (bool, error) { if exeErr != nil { logging.VLog().WithFields(logrus.Fields{ "err": exeErr, @@ -378,7 +391,7 @@ func submitTx(tx *Transaction, block *Block, ws WorldState, gas *util.Uint128, e metricsUnexpectedBehavior.Update(1) return true, err } - if err := tx.recordResultEvent(gas, exeErr, ws); err != nil { + if err := tx.recordResultEvent(gas, exeErr, ws, block, exeResult); err != nil { logging.VLog().WithFields(logrus.Fields{ "err": err, "tx": tx, @@ -438,7 +451,7 @@ func VerifyExecution(tx *Transaction, block *Block, ws WorldState) (bool, error) // step3. check payload vaild. payload, payloadErr := tx.LoadPayload() if payloadErr != nil { - return submitTx(tx, block, ws, gasUsed, payloadErr, "Failed to load payload.") + return submitTx(tx, block, ws, gasUsed, payloadErr, "Failed to load payload.", "") } // step4. calculate base gas of payload @@ -452,20 +465,20 @@ func VerifyExecution(tx *Transaction, block *Block, ws WorldState) (bool, error) "block": block, }).Error("Failed to add payload base gas, unexpected error") metricsUnexpectedBehavior.Update(1) - return submitTx(tx, block, ws, gasUsed, ErrGasCntOverflow, "Failed to add the count of base payload gas") + return submitTx(tx, block, ws, gasUsed, ErrGasCntOverflow, "Failed to add the count of base payload gas", "") } gasUsed = payloadGas if tx.gasLimit.Cmp(gasUsed) < 0 { - return submitTx(tx, block, ws, tx.gasLimit, ErrOutOfGasLimit, "Failed to check gasLimit >= txBaseGas + payloasBaseGas.") + return submitTx(tx, block, ws, tx.gasLimit, ErrOutOfGasLimit, "Failed to check gasLimit >= txBaseGas + payloasBaseGas.", "") } // step5. check balance >= limitedFee + value. and transfer minBalanceRequired, balanceErr := limitedFee.Add(tx.value) if balanceErr != nil { - return submitTx(tx, block, ws, gasUsed, ErrGasFeeOverflow, "Failed to add tx.value") + return submitTx(tx, block, ws, gasUsed, ErrGasFeeOverflow, "Failed to add tx.value", "") } if fromAcc.Balance().Cmp(minBalanceRequired) < 0 { - return submitTx(tx, block, ws, gasUsed, ErrInsufficientBalance, "Failed to check balance >= gasLimit * gasPrice + value") + return submitTx(tx, block, ws, gasUsed, ErrInsufficientBalance, "Failed to check balance >= gasLimit * gasPrice + value", "") } var transferSubErr, transferAddErr error transferSubErr = fromAcc.SubBalance(tx.value) @@ -482,7 +495,7 @@ func VerifyExecution(tx *Transaction, block *Block, ws WorldState) (bool, error) "block": block, }).Error("Failed to transfer value, unexpected error") metricsUnexpectedBehavior.Update(1) - return submitTx(tx, block, ws, gasUsed, ErrInvalidTransfer, "Failed to transfer tx.value") + return submitTx(tx, block, ws, gasUsed, ErrInvalidTransfer, "Failed to transfer tx.value", "") } // step6. calculate contract's limited gas @@ -495,23 +508,23 @@ func VerifyExecution(tx *Transaction, block *Block, ws WorldState) (bool, error) "block": block, }).Error("Failed to calculate payload's limit gas, unexpected error") metricsUnexpectedBehavior.Update(1) - return submitTx(tx, block, ws, tx.gasLimit, ErrOutOfGasLimit, "Failed to calculate payload's limit gas") + return submitTx(tx, block, ws, tx.gasLimit, ErrOutOfGasLimit, "Failed to calculate payload's limit gas", "") } // step7. execute contract. - gasExecution, _, exeErr := payload.Execute(contractLimitedGas, tx, block, ws) + gasExecution, exeResult, exeErr := payload.Execute(contractLimitedGas, tx, block, ws) // step8. calculate final gas. allGas, gasErr := gasUsed.Add(gasExecution) if gasErr != nil { - return submitTx(tx, block, ws, gasUsed, ErrGasCntOverflow, "Failed to add the fee of execution gas") + return submitTx(tx, block, ws, gasUsed, ErrGasCntOverflow, "Failed to add the fee of execution gas", "") } if tx.gasLimit.Cmp(allGas) < 0 { - return submitTx(tx, block, ws, tx.gasLimit, ErrOutOfGasLimit, "Failed to check gasLimit >= allGas") + return submitTx(tx, block, ws, tx.gasLimit, ErrOutOfGasLimit, "Failed to check gasLimit >= allGas", "") } // step9. over - return submitTx(tx, block, ws, allGas, exeErr, "Failed to execute payload") + return submitTx(tx, block, ws, allGas, exeErr, "Failed to execute payload", exeResult) } // simulateExecution simulate execution and return gasUsed, executionResult and executionErr, sysErr if occurred. @@ -555,7 +568,8 @@ func (tx *Transaction) simulateExecution(block *Block) (*SimulateResult, error) ) // try run smart contract if payload is. - if tx.data.Type == TxPayloadCallType || tx.data.Type == TxPayloadDeployType { + if tx.data.Type == TxPayloadCallType || tx.data.Type == TxPayloadDeployType || + (tx.data.Type == TxPayloadBinaryType && tx.to.Type() == ContractAddress && block.height >= AcceptFuncAvailableHeight) { // transfer value to smart contract. toAcc, err := ws.GetOrCreateUserAccount(tx.to.address) @@ -614,22 +628,46 @@ func (tx *Transaction) recordGas(gasCnt *util.Uint128, ws WorldState) error { return ws.RecordGas(tx.from.String(), gasCost) } -func (tx *Transaction) recordResultEvent(gasUsed *util.Uint128, err error, ws WorldState) error { - txEvent := &TransactionEvent{ - Hash: tx.hash.String(), - GasUsed: gasUsed.String(), - Status: TxExecutionSuccess, - } +func (tx *Transaction) recordResultEvent(gasUsed *util.Uint128, err error, ws WorldState, block *Block, exeResult string) error { - if err != nil { - txEvent.Status = TxExecutionFailed - txEvent.Error = err.Error() - if len(txEvent.Error) > MaxEventErrLength { - txEvent.Error = txEvent.Error[:MaxEventErrLength] + var txData []byte + if block.height >= RecordCallContractResultHeight { + + if len(exeResult) > MaxResultLength { + exeResult = exeResult[:MaxResultLength] + } + txEvent := &TransactionEventV2{ + Hash: tx.hash.String(), + GasUsed: gasUsed.String(), + Status: TxExecutionSuccess, + ExecuteResult: exeResult, + } + + if err != nil { + txEvent.Status = TxExecutionFailed + txEvent.Error = err.Error() + if len(txEvent.Error) > MaxEventErrLength { + txEvent.Error = txEvent.Error[:MaxEventErrLength] + } + } + txData, err = json.Marshal(txEvent) + } else { + txEvent := &TransactionEvent{ + Hash: tx.hash.String(), + GasUsed: gasUsed.String(), + Status: TxExecutionSuccess, + } + + if err != nil { + txEvent.Status = TxExecutionFailed + txEvent.Error = err.Error() + if len(txEvent.Error) > MaxEventErrLength { + txEvent.Error = txEvent.Error[:MaxEventErrLength] + } } + txData, err = json.Marshal(txEvent) } - txData, err := json.Marshal(txEvent) if err != nil { return err } @@ -711,6 +749,10 @@ func CheckContract(addr *Address, ws WorldState) (state.Account, error) { return nil, ErrNilArgument } + if addr.Type() != ContractAddress { + return nil, ErrContractCheckFailed + } + contract, err := ws.GetContractAccount(addr.Bytes()) if err != nil { return nil, err diff --git a/core/transaction_binary_payload.go b/core/transaction_binary_payload.go index 44304e3c0..28cb293b0 100644 --- a/core/transaction_binary_payload.go +++ b/core/transaction_binary_payload.go @@ -19,6 +19,8 @@ package core import ( + "fmt" + "github.com/nebulasio/go-nebulas/util" ) @@ -51,5 +53,55 @@ func (payload *BinaryPayload) BaseGasCount() *util.Uint128 { // Execute the payload in tx func (payload *BinaryPayload) Execute(limitedGas *util.Uint128, tx *Transaction, block *Block, ws WorldState) (*util.Uint128, string, error) { + if block == nil || tx == nil || tx.to == nil { + return util.NewUint128(), "", ErrNilArgument + } + + // transfer to contract + if tx.to.Type() == ContractAddress && block.height >= AcceptFuncAvailableHeight { + // payloadGasLimit <= 0, v8 engine not limit the execution instructions + if limitedGas.Cmp(util.NewUint128()) <= 0 { + return util.NewUint128(), "", ErrOutOfGasLimit + } + + // contract address is tx.to. + contract, err := CheckContract(tx.to, ws) + if err != nil { + return util.NewUint128(), "", err + } + + birthTx, err := GetTransaction(contract.BirthPlace(), ws) + if err != nil { + return util.NewUint128(), "", err + } + deploy, err := LoadDeployPayload(birthTx.data.Payload) // ToConfirm: move deploy payload in ctx. + if err != nil { + return util.NewUint128(), "", err + } + + engine, err := block.nvm.CreateEngine(block, tx, contract, ws) + if err != nil { + return util.NewUint128(), "", err + } + defer engine.Dispose() + + if err := engine.SetExecutionLimits(limitedGas.Uint64(), DefaultLimitsOfTotalMemorySize); err != nil { + return util.NewUint128(), "", err + } + + 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 + } + if exeErr == ErrExecutionFailed && len(result) > 0 { + exeErr = fmt.Errorf("Binary: %s", result) + } + if exeErr != nil { + return instructions, "", exeErr + } + } + return util.NewUint128(), "", nil } diff --git a/core/transaction_call_payload.go b/core/transaction_call_payload.go index be899ad3c..cacda201d 100644 --- a/core/transaction_call_payload.go +++ b/core/transaction_call_payload.go @@ -115,7 +115,7 @@ func (payload *CallPayload) Execute(limitedGas *util.Uint128, tx *Transaction, b if err != nil { return util.NewUint128(), "", err } - if exeErr != nil && exeErr == ErrExecutionFailed && len(result) > 0 { + if exeErr == ErrExecutionFailed && len(result) > 0 { exeErr = fmt.Errorf("Call: %s", result) } return instructions, result, exeErr diff --git a/core/types.go b/core/types.go index 1613160b3..d514f1917 100644 --- a/core/types.go +++ b/core/types.go @@ -51,6 +51,11 @@ const ( SourceTypeTypeScript = "ts" ) +// Const +const ( + ContractAcceptFunc = "accept" +) + var ( // PublicFuncNameChecker in smart contract PublicFuncNameChecker = regexp.MustCompile("^[a-zA-Z$][A-Za-z0-9_$]*$") @@ -75,6 +80,7 @@ var ( ErrCannotFindBlockAtGivenHeight = errors.New("cannot find a block at given height which is less than tail block's height") ErrInvalidBlockCannotFindParentInLocalAndTryDownload = errors.New("invalid block received, download its parent from others") ErrInvalidBlockCannotFindParentInLocalAndTrySync = errors.New("invalid block received, sync its parent from others") + ErrBlockNotFound = errors.New("block not found in blockchain cache nor chain") ErrInvalidConfigChainID = errors.New("invalid chainID, genesis chainID not equal to chainID in config") ErrCannotLoadGenesisConf = errors.New("cannot load genesis conf") @@ -90,6 +96,8 @@ var ( ErrDoubleSealBlock = errors.New("cannot seal a block twice") ErrDuplicatedBlock = errors.New("duplicated block") ErrDoubleBlockMinted = errors.New("double block minted") + ErrVRFProofFailed = errors.New("VRF proof failed") + ErrInvalidBlockRandom = errors.New("invalid block random") ErrInvalidChainID = errors.New("invalid transaction chainID") ErrInvalidTransactionSigner = errors.New("invalid transaction signer") @@ -212,6 +220,8 @@ type Consensus interface { GenesisConsensusState(*BlockChain, *corepb.Genesis) (state.ConsensusState, error) CheckTimeout(*Block) bool CheckDoubleMint(*Block) bool + + NumberOfBlocksInDynasty() uint64 } // SyncService interface of sync service @@ -235,6 +245,7 @@ type AccountManager interface { SignHash(*Address, byteutils.Hash, keystore.Algorithm) ([]byte, error) SignBlock(*Address, *Block) error + GenerateRandomSeed(*Address, ...[]byte) ([]byte, []byte, error) SignTransaction(*Address, *Transaction) error SignTransactionWithPassphrase(*Address, *Transaction, []byte) error diff --git a/crypto/keystore/secp256k1/vrf/secp256k1VRF/secp256k1VRF.go b/crypto/keystore/secp256k1/vrf/secp256k1VRF/secp256k1VRF.go new file mode 100644 index 000000000..7f53c37eb --- /dev/null +++ b/crypto/keystore/secp256k1/vrf/secp256k1VRF/secp256k1VRF.go @@ -0,0 +1,331 @@ +// Copyright 2016 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Implements a verifiable random function using curve secp256k1. + +package secp256k1VRF + +// Discrete Log based VRF from Appendix A of CONIKS: +// http://www.jbonneau.com/doc/MBBFF15-coniks.pdf +// based on "Unique Ring Signatures, a Practical Construction" +// http://fc13.ifca.ai/proc/5-1.pdf + +import ( + "bytes" + "crypto" + "crypto/ecdsa" + "crypto/hmac" + "crypto/rand" + "crypto/sha256" + "crypto/sha512" + "encoding/binary" + "errors" + "io" + "math/big" + + "github.com/nebulasio/go-nebulas/crypto/keystore/secp256k1" + "github.com/nebulasio/go-nebulas/crypto/keystore/secp256k1/bitelliptic" + + "github.com/nebulasio/go-nebulas/crypto/keystore/secp256k1/vrf" +) + +var ( + curve = bitelliptic.S256() + params = curve.Params() + + // ErrPointNotOnCurve occurs when a public key is not on the curve. + ErrPointNotOnCurve = errors.New("point is not on the P256 curve") + // ErrWrongKeyType occurs when a key is not an ECDSA key. + ErrWrongKeyType = errors.New("not an ECDSA key") + // ErrNoPEMFound occurs when attempting to parse a non PEM data structure. + ErrNoPEMFound = errors.New("no PEM block found") + // ErrInvalidVRF occurs when the VRF does not validate. + ErrInvalidVRF = errors.New("invalid VRF proof") + // ErrEvaluateFailed fail + ErrEvaluateFailed = errors.New("failed to evaluate VRF") +) + +// PublicKey holds a public VRF key. +type PublicKey struct { + *ecdsa.PublicKey +} + +// PrivateKey holds a private VRF key. +type PrivateKey struct { + *ecdsa.PrivateKey +} + +// GenerateKey generates a fresh keypair for this VRF +func GenerateKey() (vrf.PrivateKey, vrf.PublicKey) { + key, err := ecdsa.GenerateKey(curve, rand.Reader) + if err != nil { + return nil, nil + } + + return &PrivateKey{key}, &PublicKey{&key.PublicKey} +} + +// H1 hashes m to a curve point +func H1(m []byte) (x, y *big.Int) { + h := sha512.New() + var i uint32 + byteLen := (params.BitSize + 7) >> 3 + for x == nil && i < 100 { + // TODO: Use a NIST specified DRBG. + h.Reset() + binary.Write(h, binary.BigEndian, i) + h.Write(m) + r := []byte{2} // Set point encoding to "compressed", y=0. + r = h.Sum(r) + x, y = Unmarshal(curve, r[:byteLen+1]) + i++ + } + return +} + +var one = big.NewInt(1) + +// H2 hashes to an integer [1,N-1] +func H2(m []byte) *big.Int { + // NIST SP 800-90A § A.5.1: Simple discard method. + byteLen := (params.BitSize + 7) >> 3 + h := sha512.New() + for i := uint32(0); ; i++ { + // TODO: Use a NIST specified DRBG. + h.Reset() + binary.Write(h, binary.BigEndian, i) + h.Write(m) + b := h.Sum(nil) + k := new(big.Int).SetBytes(b[:byteLen]) + if k.Cmp(new(big.Int).Sub(params.N, one)) == -1 { + return k.Add(k, one) + } + } +} + +// Evaluate returns the verifiable unpredictable function evaluated at m +func (k PrivateKey) Evaluate(m []byte) (index [32]byte, proof []byte) { + nilIndex := [32]byte{} + // Prover chooses r <-- [1,N-1] + r, _, _, err := generateKeyFromCurve(curve, rand.Reader) + if err != nil { + return nilIndex, nil + } + ri := new(big.Int).SetBytes(r) + + // H = H1(m) + Hx, Hy := H1(m) + + // VRF_k(m) = [k]H + sHx, sHy := curve.ScalarMult(Hx, Hy, k.D.Bytes()) + + // vrf := elliptic.Marshal(curve, sHx, sHy) // 65 bytes. + vrf := curve.Marshal(sHx, sHy) // 65 bytes. + + // G is the base point + // s = H2(G, H, [k]G, VRF, [r]G, [r]H) + rGx, rGy := curve.ScalarBaseMult(r) + rHx, rHy := curve.ScalarMult(Hx, Hy, r) + var b bytes.Buffer + b.Write(curve.Marshal(params.Gx, params.Gy)) + b.Write(curve.Marshal(Hx, Hy)) + b.Write(curve.Marshal(k.PublicKey.X, k.PublicKey.Y)) + b.Write(vrf) + b.Write(curve.Marshal(rGx, rGy)) + b.Write(curve.Marshal(rHx, rHy)) + s := H2(b.Bytes()) + + // t = r−s*k mod N + t := new(big.Int).Sub(ri, new(big.Int).Mul(s, k.D)) + t.Mod(t, params.N) + + // Index = H(vrf) + index = sha256.Sum256(vrf) + + // Write s, t, and vrf to a proof blob. Also write leading zeros before s and t + // if needed. + var buf bytes.Buffer + buf.Write(make([]byte, 32-len(s.Bytes()))) + buf.Write(s.Bytes()) + buf.Write(make([]byte, 32-len(t.Bytes()))) + buf.Write(t.Bytes()) + buf.Write(vrf) + + return index, buf.Bytes() +} + +// ProofToHash asserts that proof is correct for m and outputs index. +func (pk *PublicKey) ProofToHash(m, proof []byte) (index [32]byte, err error) { + nilIndex := [32]byte{} + // verifier checks that s == H2(m, [t]G + [s]([k]G), [t]H1(m) + [s]VRF_k(m)) + if got, want := len(proof), 64+65; got != want { + return nilIndex, ErrInvalidVRF + } + + // Parse proof into s, t, and vrf. + s := proof[0:32] + t := proof[32:64] + vrf := proof[64 : 64+65] + + // uHx, uHy := elliptic.Unmarshal(curve, vrf) + uHx, uHy := curve.Unmarshal(vrf) //////??? + if uHx == nil { + return nilIndex, ErrInvalidVRF + } + + // [t]G + [s]([k]G) = [t+ks]G + tGx, tGy := curve.ScalarBaseMult(t) + ksGx, ksGy := curve.ScalarMult(pk.X, pk.Y, s) + tksGx, tksGy := curve.Add(tGx, tGy, ksGx, ksGy) + + // H = H1(m) + // [t]H + [s]VRF = [t+ks]H + Hx, Hy := H1(m) + tHx, tHy := curve.ScalarMult(Hx, Hy, t) + sHx, sHy := curve.ScalarMult(uHx, uHy, s) + tksHx, tksHy := curve.Add(tHx, tHy, sHx, sHy) + + // H2(G, H, [k]G, VRF, [t]G + [s]([k]G), [t]H + [s]VRF) + // = H2(G, H, [k]G, VRF, [t+ks]G, [t+ks]H) + // = H2(G, H, [k]G, VRF, [r]G, [r]H) + var b bytes.Buffer + b.Write(curve.Marshal(params.Gx, params.Gy)) + b.Write(curve.Marshal(Hx, Hy)) + b.Write(curve.Marshal(pk.X, pk.Y)) + b.Write(vrf) + b.Write(curve.Marshal(tksGx, tksGy)) + b.Write(curve.Marshal(tksHx, tksHy)) + h2 := H2(b.Bytes()) + + // Left pad h2 with zeros if needed. This will ensure that h2 is padded + // the same way s is. + var buf bytes.Buffer + buf.Write(make([]byte, 32-len(h2.Bytes()))) + buf.Write(h2.Bytes()) + + if !hmac.Equal(s, buf.Bytes()) { + return nilIndex, ErrInvalidVRF + } + return sha256.Sum256(vrf), nil +} + +// NewFromWrappedKey creates a VRF signer object from an encrypted private key. +// The opaque private key must resolve to an `ecdsa.PrivateKey` in order to work. +// func NewFromWrappedKey(ctx context.Context, wrapped proto.Message) (vrf.PrivateKey, error) { +// // Unwrap. +// signer, err := keys.NewSigner(ctx, wrapped) +// if err != nil { +// return nil, err +// } + +// switch key := signer.(type) { +// case *ecdsa.PrivateKey: +// return NewVRFSigner(key) +// default: +// return nil, fmt.Errorf("NewSigner().type: %T, want ecdsa.PrivateKey", key) +// } +// } + +// NewVRFSigner creates a signer object from a private key. +func NewVRFSigner(key *ecdsa.PrivateKey) (vrf.PrivateKey, error) { + if *(key.Params()) != *curve.Params() { + return nil, ErrPointNotOnCurve + } + if !curve.IsOnCurve(key.X, key.Y) { + return nil, ErrPointNotOnCurve + } + return &PrivateKey{key}, nil +} + +// Public returns the corresponding public key as bytes. +func (k PrivateKey) Public() crypto.PublicKey { + return &k.PublicKey +} + +// NewVRFVerifier creates a verifier object from a public key. +func NewVRFVerifier(pubkey *ecdsa.PublicKey) (vrf.PublicKey, error) { + if *(pubkey.Params()) != *curve.Params() { + return nil, ErrPointNotOnCurve + } + if !curve.IsOnCurve(pubkey.X, pubkey.Y) { + return nil, ErrPointNotOnCurve + } + return &PublicKey{pubkey}, nil +} + +// NewVRFSignerFromPEM creates a vrf private key from a PEM data structure. +// func NewVRFSignerFromPEM(b []byte) (vrf.PrivateKey, error) { +// p, _ := pem.Decode(b) +// if p == nil { +// return nil, ErrNoPEMFound +// } +// return NewVRFSignerFromRawKey(p.Bytes) +// } + +// NewVRFSignerFromRawKey returns the private key from a raw private key bytes. +func NewVRFSignerFromRawKey(b []byte) (vrf.PrivateKey, error) { + k, err := secp256k1.ToECDSAPrivateKey(b) + if err != nil { + return nil, err + } + return NewVRFSigner(k) +} + +// NewVRFVerifierFromPEM creates a vrf public key from a PEM data structure. +// func NewVRFVerifierFromPEM(b []byte) (vrf.PublicKey, error) { +// p, _ := pem.Decode(b) +// if p == nil { +// return nil, ErrNoPEMFound +// } +// return NewVRFVerifierFromRawKey(p.Bytes) +// } + +// NewVRFVerifierFromRawKey returns the public key from a raw public key bytes. +func NewVRFVerifierFromRawKey(b []byte) (vrf.PublicKey, error) { + k, err := secp256k1.ToECDSAPublicKey(b) + if err != nil { + return nil, err + } + return NewVRFVerifier(k) +} + +var mask = []byte{0xff, 0x1, 0x3, 0x7, 0xf, 0x1f, 0x3f, 0x7f} + +func generateKeyFromCurve(curve *bitelliptic.BitCurve, rand io.Reader) (priv []byte, x, y *big.Int, err error) { + N := curve.Params().N + bitSize := N.BitLen() + byteLen := (bitSize + 7) >> 3 + priv = make([]byte, byteLen) + + for x == nil { + _, err = io.ReadFull(rand, priv) + if err != nil { + return + } + // We have to mask off any excess bits in the case that the size of the + // underlying field is not a whole number of bytes. + priv[0] &= mask[bitSize%8] + // This is because, in tests, rand will return all zeros and we don't + // want to get the point at infinity and loop forever. + priv[1] ^= 0x42 + + // If the scalar is out of range, sample another random number. + if new(big.Int).SetBytes(priv).Cmp(N) >= 0 { + continue + } + + x, y = curve.ScalarBaseMult(priv) + } + return +} diff --git a/crypto/keystore/secp256k1/vrf/secp256k1VRF/secp256k1VRF_test.go b/crypto/keystore/secp256k1/vrf/secp256k1VRF/secp256k1VRF_test.go new file mode 100644 index 000000000..5d87a51e5 --- /dev/null +++ b/crypto/keystore/secp256k1/vrf/secp256k1VRF/secp256k1VRF_test.go @@ -0,0 +1,362 @@ +// Copyright 2016 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package secp256k1VRF + +import ( + "bytes" + "crypto/rand" + "encoding/hex" + "fmt" + "math" + "testing" + "time" + + "github.com/nebulasio/go-nebulas/crypto/keystore/secp256k1" + "github.com/nebulasio/go-nebulas/util/byteutils" + // _ "github.com/google/trillian/crypto/keys/der/proto" +) + +const ( + // private key in hex + privKey = `b02b430d4a9d7120b65038452a6da3f3c716829e5be3665adf934d4798d96ed7` + // public key in hex + pubKey = `04e4d0dde330c0b8d8d8b1b2071aa75c3e94f200a3d11ca1d908644eee50c8833a816dc0b2d003fc66187ef6750a56e1b3004d32e6159008400ab92f2ded7b4544` +) + +func TestH1(t *testing.T) { + for i := 0; i < 10000; i++ { + m := make([]byte, 100) + if _, err := rand.Read(m); err != nil { + t.Fatalf("Failed generating random message: %v", err) + } + x, y := H1(m) + if x == nil { + t.Errorf("H1(%v)=%v, want curve point", m, x) + } + // attention: should not use curve.Params(), that will use elliptic.IsOnCurve() + if got := curve.IsOnCurve(x, y); !got { + t.Errorf("H1(%v)=[%v, %v], is not on curve", m, x, y) + } + } +} + +func TestH2(t *testing.T) { + l := 32 + for i := 0; i < 10000; i++ { + m := make([]byte, 100) + if _, err := rand.Read(m); err != nil { + t.Fatalf("Failed generating random message: %v", err) + } + x := H2(m) + if got := len(x.Bytes()); got < 1 || got > l { + t.Errorf("len(h2(%v)) = %v, want: 1 <= %v <= %v", m, got, got, l) + } + } +} + +func TestEmmm(t *testing.T) { + k, pk := GenerateKey() + m1 := []byte("da30b4ed14affb62b3719fb5e6952d3733e84e53fe6e955f8e46da503300c985") + index1, proof1 := k.Evaluate(m1) + fmt.Println("evaluate index = ", index1) + fmt.Println("evaluate proof = ", proof1) + + start := time.Now() + for i := 0; i < 100; i++ { + _, _ = k.Evaluate(m1) + } + end := time.Now() + fmt.Println("evaluate x100 total time: ", end.Sub(start)) + + index, _ := pk.ProofToHash(m1, proof1) + fmt.Println("proof index = ", index) + + start = time.Now() + for i := 0; i < 100; i++ { + _, _ = pk.ProofToHash(m1, proof1) + } + end = time.Now() + fmt.Println("proof x100 total time: ", end.Sub(start)) +} + +func TestVRF(t *testing.T) { + k, pk := GenerateKey() + + m1 := []byte("data1") + m2 := []byte("data2") + m3 := []byte("data2") + index1, proof1 := k.Evaluate(m1) + index2, proof2 := k.Evaluate(m2) + index3, proof3 := k.Evaluate(m3) + for _, tc := range []struct { + m []byte + index [32]byte + proof []byte + err error + }{ + {m1, index1, proof1, nil}, + {m2, index2, proof2, nil}, + {m3, index3, proof3, nil}, + {m3, index3, proof2, nil}, + {m3, index3, proof1, ErrInvalidVRF}, + } { + index, err := pk.ProofToHash(tc.m, tc.proof) + if got, want := err, tc.err; got != want { + t.Errorf("ProofToHash(%s, %x): %v, want %v", tc.m, tc.proof, got, want) + } + if err != nil { + continue + } + if got, want := index, tc.index; got != want { + t.Errorf("ProofToInex(%s, %x): %x, want %x", tc.m, tc.proof, got, want) + } + } +} + +func TestProofToHash(t *testing.T) { + bytes, _ := byteutils.FromHex(pubKey) + pk, err := NewVRFVerifierFromRawKey(bytes) + if err != nil { + t.Errorf("NewVRFSigner failure: %v", err) + } + + for _, tc := range []struct { + m []byte + index [32]byte + proof []byte + }{ + { + m: []byte("data1"), + index: h2i("a2f4f844d46240a86790c177f21422f430b2803c7590f32625079fc13a5fe601"), + proof: h2b("cc23d0e1e01a20bcee479e944c94febabb8e762fa64b9443fc9dc31d3332e3a7024f4adc2cda4e8847fe67f47ab0084b677996e9325d31840531a2f91d6a5d7d04e54044c12dd5ab7b90a57117a85d6307125496ada896d9823c860c4f492c0096c714705d58ee7d66ee6cffb5f1320c5eab7f92490b0f5759145588efa0b0537d"), + }, + { + m: []byte("data2"), + index: h2i("008a288a33a2620458a26b6c995d9c16ca46c293562db76985bd1b2a159efc76"), + proof: h2b("888e0d3191af542c40d0d8b15255e106a133ec9b219b6e26900e07a252e6ab60e510423c34bf74cc602ae2be214bffadfd639793d0a3dccd0e7303be8d0de57604322cef265dfe906cebf30de74b14aa33723435eccea3153fedb5bea70e5c58a8969af97c27e50223bc3b9a8dd8f4a60ec363a78c957f366af075cf83cc43e61c"), + }, + { + m: []byte("data3"), + index: h2i("9bb53b519519a85c8c6c6739349168c42ae208aed7dadeababf5a067a6ac1313"), + proof: h2b("96004eb1450c68fcb1ac83e0f09c5311089829762a5e8aecdba1c51d703250d79bc9ffeb72c0c5645da6c3d2d59a5c6428b1d3a0075d75b89bae8b539453e3af044a472b26f259bd5a84f05ec8fe1d7858d6f5606adcb6febeef113a2ff4ff69d5166ebbd3c3a78c451d751490eeb37fd39358fb2fad8ae218e3fc5177fe2e9b37"), + }, + } { + + bytes, _ := byteutils.FromHex(privKey) + k, err := NewVRFSignerFromRawKey(bytes) + idx, proof := k.Evaluate(tc.m) + fmt.Println("=======") + fmt.Println(byteutils.Hex(idx[0:])) + fmt.Println(byteutils.Hex(proof)) + + index, err := pk.ProofToHash(tc.m, tc.proof) + if err != nil { + t.Errorf("ProofToHash(%s, %x): %v, want nil", tc.m, tc.proof, err) + continue + } + if got, want := index, tc.index; got != want { + t.Errorf("ProofToHash(%s, %x): %x, want %x", tc.m, tc.proof, got, want) + } + } +} + +func TestReadFromOpenSSL(t *testing.T) { + for _, tc := range []struct { + priv string + pub string + }{ + {privKey, pubKey}, + } { + // Private VRF Key + bytes, _ := byteutils.FromHex(tc.priv) + signer, err := NewVRFSignerFromRawKey(bytes) + if err != nil { + t.Errorf("NewVRFSigner failure: %v", err) + } + + // Public VRF key + bytes, _ = byteutils.FromHex(tc.pub) + verifier, err := NewVRFVerifierFromRawKey(bytes) + if err != nil { + t.Errorf("NewVRFSigner failure: %v", err) + } + + // Evaluate and verify. + m := []byte("M") + _, proof := signer.Evaluate(m) + if _, err := verifier.ProofToHash(m, proof); err != nil { + t.Errorf("Failed verifying VRF proof") + } + } +} + +func TestRightTruncateProof(t *testing.T) { + k, pk := GenerateKey() + + data := []byte("data") + _, proof := k.Evaluate(data) + proofLen := len(proof) + for i := 0; i < proofLen; i++ { + proof = proof[:len(proof)-1] + if _, err := pk.ProofToHash(data, proof); err == nil { + t.Errorf("Verify unexpectedly succeeded after truncating %v bytes from the end of proof", i) + } + } +} + +func TestLeftTruncateProof(t *testing.T) { + k, pk := GenerateKey() + + data := []byte("data") + _, proof := k.Evaluate(data) + proofLen := len(proof) + for i := 0; i < proofLen; i++ { + proof = proof[1:] + if _, err := pk.ProofToHash(data, proof); err == nil { + t.Errorf("Verify unexpectedly succeeded after truncating %v bytes from the beginning of proof", i) + } + } +} + +func TestBitFlip(t *testing.T) { + k, pk := GenerateKey() + + data := []byte("data") + _, proof := k.Evaluate(data) + for i := 0; i < len(proof)*8; i++ { + // Flip bit in position i. + if _, err := pk.ProofToHash(data, flipBit(proof, i)); err == nil { + t.Errorf("Verify unexpectedly succeeded after flipping bit %v of vrf", i) + } + } +} + +func flipBit(a []byte, pos int) []byte { + index := int(math.Floor(float64(pos) / 8)) + b := byte(a[index]) + b ^= (1 << uint(math.Mod(float64(pos), 8.0))) + + var buf bytes.Buffer + buf.Write(a[:index]) + buf.Write([]byte{b}) + buf.Write(a[index+1:]) + return buf.Bytes() +} + +func TestVectors(t *testing.T) { + bytes, _ := byteutils.FromHex(privKey) + k, err := NewVRFSignerFromRawKey(bytes) + if err != nil { + t.Errorf("NewVRFSigner failure: %v", err) + } + bytes, _ = byteutils.FromHex(pubKey) + pk, err := NewVRFVerifierFromRawKey(bytes) + if err != nil { + t.Errorf("NewVRFSigner failure: %v", err) + } + for _, tc := range []struct { + m []byte + index [32]byte + }{ + { + m: []byte("test"), + index: h2i("c095a258b89a5fbf0790e45cd2b1a31c1723f0f99c7df3df98e03eef2a4a25af"), + }, + { + m: nil, + index: h2i("19a1da136a3dadfd4ffb2e95d0b236b72e7bd448541a46fde595acd5052775cb"), + }, + } { + index, proof := k.Evaluate(tc.m) + if got, want := index, tc.index; got != want { + t.Errorf("Evaluate(%s).Index: %x, want %x", tc.m, got, want) + } + index2, err := pk.ProofToHash(tc.m, proof) + if err != nil { + t.Errorf("ProofToHash(%s): %v", tc.m, err) + } + if got, want := index2, index; got != want { + t.Errorf("ProofToHash(%s): %x, want %x", tc.m, got, want) + } + } +} + +func h2i(h string) [32]byte { + b, err := hex.DecodeString(h) + if err != nil { + panic("Invalid hex") + } + var i [32]byte + copy(i[:], b) + return i +} + +func h2b(h string) []byte { + b, err := hex.DecodeString(h) + if err != nil { + panic("Invalid hex") + } + return b +} + +func Test256VRF(t *testing.T) { + + seckey, err := byteutils.FromHex(privKey) + if err != nil { + t.Errorf("load priv err: %v", err) + } + ecdsaPriv, err := secp256k1.ToECDSAPrivateKey(seckey) + if err != nil { + t.Errorf("ecdsa err: %v", err) + } + + signer, err := NewVRFSigner(ecdsaPriv) + if err != nil { + t.Errorf("new signer err: %v", err) + } + + data := []byte("b10c1203d5ae6d4d069d5f520eb060f2f5fb74e942f391e7cadbc2b5148dfbcb") + sIndex, proof := signer.Evaluate(data) + + seckeyPub, err := byteutils.FromHex(pubKey) + + priv := new(secp256k1.PrivateKey) + err = priv.Decode(seckey) + if err != nil { + t.Errorf("decode priv err: %v", err) + } + + epub, err := priv.PublicKey().Encoded() + if err != nil { + t.Errorf("encode pub err: %v", err) + } + if !bytes.Equal(seckeyPub, epub) { + t.Errorf("mismatched priv/pub err: %v", err) + } + + verifier, err := NewVRFVerifierFromRawKey(seckeyPub) + if err != nil { + t.Errorf("new verifier err: %v", err) + } + + vIndex, err := verifier.ProofToHash(data, proof) + if err != nil { + t.Errorf("exec proof err: %v", err) + } + + if !bytes.Equal(sIndex[0:], vIndex[0:]) { + t.Errorf("verification failed") + } +} diff --git a/crypto/keystore/secp256k1/vrf/secp256k1VRF/unmarshal.go b/crypto/keystore/secp256k1/vrf/secp256k1VRF/unmarshal.go new file mode 100644 index 000000000..c30ac11b9 --- /dev/null +++ b/crypto/keystore/secp256k1/vrf/secp256k1VRF/unmarshal.go @@ -0,0 +1,89 @@ +// Copyright 2016 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package secp256k1VRF + +// This file implements compressed point unmarshaling. Preferably this +// functionality would be in a standard library. Code borrowed from: +// https://go-review.googlesource.com/#/c/1883/2/src/crypto/elliptic/elliptic.go + +import ( + "crypto/elliptic" + "math/big" +) + +// Unmarshal a compressed point in the form specified in section 4.3.6 of ANSI X9.62. +func Unmarshal(curve elliptic.Curve, data []byte) (x, y *big.Int) { + byteLen := (curve.Params().BitSize + 7) >> 3 + if (data[0] &^ 1) != 2 { + return // unrecognized point encoding + } + if len(data) != 1+byteLen { + return + } + + // Based on Routine 2.2.4 in NIST Mathematical routines paper + params := curve.Params() + tx := new(big.Int).SetBytes(data[1 : 1+byteLen]) + y2 := y2(params, tx) + sqrt := defaultSqrt + ty := sqrt(y2, params.P) + if ty == nil { + return // "y^2" is not a square: invalid point + } + var y2c big.Int + y2c.Mul(ty, ty).Mod(&y2c, params.P) + if y2c.Cmp(y2) != 0 { + return // sqrt(y2)^2 != y2: invalid point + } + if ty.Bit(0) != uint(data[0]&1) { + ty.Sub(params.P, ty) + } + + x, y = tx, ty // valid point: return it + return +} + +// Use the curve equation to calculate y² given x. +// only applies to curves of the form y² = x³ - 3x + b. +func y2(curve *elliptic.CurveParams, x *big.Int) *big.Int { + + // y² = x³ - 3x + b + // x3 := new(big.Int).Mul(x, x) + // x3.Mul(x3, x) + + // threeX := new(big.Int).Lsh(x, 1) + // threeX.Add(threeX, x) + + // x3.Sub(x3, threeX) + // x3.Add(x3, curve.B) + // x3.Mod(x3, curve.P) + + // change to bitelliptic : y² = x³ + b + x3 := new(big.Int).Mul(x, x) + x3.Mul(x3, x) + + x3.Add(x3, curve.B) + x3.Mod(x3, curve.P) + + return x3 +} + +func defaultSqrt(x, p *big.Int) *big.Int { + var r big.Int + if nil == r.ModSqrt(x, p) { + return nil // x is not a square + } + return &r +} diff --git a/crypto/keystore/secp256k1/vrf/vrf.go b/crypto/keystore/secp256k1/vrf/vrf.go new file mode 100644 index 000000000..28c522254 --- /dev/null +++ b/crypto/keystore/secp256k1/vrf/vrf.go @@ -0,0 +1,35 @@ +// Copyright 2016 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package vrf defines the interface to a verifiable random function. + +// http://ieeexplore.ieee.org/stamp/stamp.jsp?tp=&arnumber=814584 + +package vrf + +import "crypto" + +// PrivateKey supports evaluating the VRF function. +type PrivateKey interface { + // Evaluate returns the output of H(f_k(m)) and its proof. + Evaluate(m []byte) (index [32]byte, proof []byte) + // Public returns the corresponding public key. + Public() crypto.PublicKey +} + +// PublicKey supports verifying output from the VRF function. +type PublicKey interface { + // ProofToHash verifies the NP-proof supplied by Proof and outputs Index. + ProofToHash(m, proof []byte) (index [32]byte, err error) +} diff --git a/crypto/keystore/secp256k1/vrf/vrf_test.go b/crypto/keystore/secp256k1/vrf/vrf_test.go new file mode 100644 index 000000000..5a5fcfe8d --- /dev/null +++ b/crypto/keystore/secp256k1/vrf/vrf_test.go @@ -0,0 +1,51 @@ +// 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 . +// + +package vrf + +import ( + "fmt" + "testing" + + "github.com/nebulasio/go-nebulas/crypto" + "github.com/nebulasio/go-nebulas/crypto/keystore" + "github.com/nebulasio/go-nebulas/util/byteutils" +) + +const ( + // private key in hex + privKey = `b02b430d4a9d7120b65038452a6da3f3c716829e5be3665adf934d4798d96ed7` + // public key in hex + pubKey = `04e4d0dde330c0b8d8d8b1b2071aa75c3e94f200a3d11ca1d908644eee50c8833a816dc0b2d003fc66187ef6750a56e1b3004d32e6159008400ab92f2ded7b4544` +) + +func TestVRF(t *testing.T) { + priv, _ := crypto.NewPrivateKey(keystore.SECP256K1, nil) + seckey, err := priv.Encoded() + if err != nil { + t.Errorf("new priv err: %v", err) + } + seckeypub, err := priv.PublicKey().Encoded() + if err != nil { + t.Errorf("pub of new priv err: %v", err) + } + fmt.Println("1:", byteutils.Hex(seckey)) + fmt.Println("2:", byteutils.Hex(seckeypub)) + +} diff --git a/metrics/metrics.go b/metrics/metrics.go index 213686136..776167e67 100644 --- a/metrics/metrics.go +++ b/metrics/metrics.go @@ -95,11 +95,14 @@ func collectSystemMetrics() { } allocs := metrics.GetOrRegisterMeter("system_allocs", nil) + // totalAllocs := metrics.GetOrRegisterMeter("system_total_allocs", nil) sys := metrics.GetOrRegisterMeter("system_sys", nil) frees := metrics.GetOrRegisterMeter("system_frees", nil) heapInuse := metrics.GetOrRegisterMeter("system_heapInuse", nil) stackInuse := metrics.GetOrRegisterMeter("system_stackInuse", nil) + releases := metrics.GetOrRegisterMeter("system_release", nil) + for i := 1; ; i++ { select { case <-quitCh: @@ -107,10 +110,13 @@ func collectSystemMetrics() { default: runtime.ReadMemStats(memstats[i%2]) allocs.Mark(int64(memstats[i%2].Alloc - memstats[(i-1)%2].Alloc)) + sys.Mark(int64(memstats[i%2].Sys - memstats[(i-1)%2].Sys)) frees.Mark(int64(memstats[i%2].Frees - memstats[(i-1)%2].Frees)) heapInuse.Mark(int64(memstats[i%2].HeapInuse - memstats[(i-1)%2].HeapInuse)) stackInuse.Mark(int64(memstats[i%2].StackInuse - memstats[(i-1)%2].StackInuse)) + releases.Mark(int64(memstats[i%2].HeapReleased - memstats[(i-1)%2].HeapReleased)) + time.Sleep(2 * time.Second) } } diff --git a/neblet/neblet.go b/neblet/neblet.go index 9f76b15fe..6c24ea42e 100644 --- a/neblet/neblet.go +++ b/neblet/neblet.go @@ -87,6 +87,7 @@ func New(config *nebletpb.Config) (*Neblet, error) { logging.CLog().Error("Failed to find chain config in config file") return nil, ErrConfigShouldHasChain } + var err error n.genesis, err = core.LoadGenesisConf(config.Chain.Genesis) if err != nil { diff --git a/nebtestkit/cases/contract/contract.bankvault.test.js b/nebtestkit/cases/contract/contract.bankvault.test.js index 7a704e0bd..f8077bb02 100644 --- a/nebtestkit/cases/contract/contract.bankvault.test.js +++ b/nebtestkit/cases/contract/contract.bankvault.test.js @@ -2,7 +2,7 @@ var HttpRequest = require("../../node-request"); -var Wallet = require("../../../cmd/console/neb.js/lib/wallet"); +var Wallet = require("nebulas"); var Account = Wallet.Account; var Transaction = Wallet.Transaction; var Utils = Wallet.Utils; @@ -24,6 +24,7 @@ var coinState; var redeploy = process.env.REDEPLOY || true; var scriptType = process.env.script || 'js'; var env = process.env.NET || 'local'; +var blockInterval = 15; // mocha cases/contract/xxx testneb1 -t 200000 var args = process.argv.splice(2); @@ -36,8 +37,8 @@ console.log("env:", env); if (env === 'local') { neb.setRequest(new HttpRequest("http://127.0.0.1:8685"));//https://testnet.nebulas.io ChainID = 100; - sourceAccount = new Wallet.Account("a6e5eb290e1438fce79f5cb8774a72621637c2c9654c8b2525ed1d7e4e73653f"); - coinbase = "eb31ad2d8a89a0ca6935c308d5425730430bc2d63f2573b8"; + sourceAccount = new Wallet.Account("1d3fe06a53919e728315e2ccca41d4aa5b190845a79007797517e62dbc0df454"); + coinbase = "n1QZMXSZtW7BUerroSms4axNfyBGyFGkrh5"; if (!redeploy) { contractAddr = "e5a02995444628cf7935e3ef8b613299a2d97e6d188ce808"; //js // contractAddr = "557fccaf2f05d4b46156e9b98ca9726f1c9e91d95d3830a7"; //ts @@ -55,8 +56,8 @@ if (env === 'local') { } else if (env === "testneb2") { neb.setRequest(new HttpRequest("http://34.205.26.12:8685")); ChainID = 1002; - sourceAccount = new Wallet.Account("43181d58178263837a9a6b08f06379a348a5b362bfab3631ac78d2ac771c5df3"); - coinbase = "0b9cd051a6d7129ab44b17833c63fe4abead40c3714cde6d"; + sourceAccount = new Wallet.Account("25a3a441a34658e7a595a0eda222fa43ac51bd223017d17b420674fb6d0a4d52"); + coinbase = "n1SAeQRVn33bamxN4ehWUT7JGdxipwn8b17"; if (!redeploy) { contractAddr = ""; } @@ -83,11 +84,12 @@ var initFromBalance = 10; * 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 = 100; +var maxCheckTime = 15; var checkTimes = 0; var beginCheckTime; function checkTransaction(hash, callback) { + console.log("==>checkTransaction"); if (checkTimes === 0) { beginCheckTime = new Date().getTime(); } @@ -104,29 +106,32 @@ function checkTransaction(hash, callback) { if (resp.status === 2) { setTimeout(function(){ checkTransaction(hash, callback) - }, 2000) + }, 5000) } else { checkTimes = 0; var endCheckTime = new Date().getTime(); console.log("check tx time: : " + (endCheckTime - beginCheckTime) / 1000); callback(resp); + return } }).catch(function (err) { console.log("fail to get tx receipt hash: '" + hash + "' probably being packing, continue checking...") - console.log(err.error) + console.log(err); + console.log(err.error); setTimeout(function(){ checkTransaction(hash, callback) - }, 2000) + }, 5000) }); } function deployContract(testInput, done) { + console.log("==> deployContract"); neb.api.getAccountState(from.getAddressString()).then(function(state){ fromState = state; console.log("from state: " + JSON.stringify(fromState)); }).then(function(){ - var filepath = "./nf/nvm/test/bank_vault_contract." + testInput.contractType; + var filepath = "../nf/nvm/test/bank_vault_contract." + testInput.contractType; console.log("deploying contract: " + filepath); var bankvault = FS.readFileSync(filepath, "utf-8"); var contract = { @@ -142,6 +147,7 @@ function deployContract(testInput, done) { console.log(err.error); done(err); }).then(function(resp){ + console.log("111: result", resp); expect(resp).to.be.have.property('txhash'); expect(resp).to.be.have.property('contract_address'); contractAddr = resp.contract_address; @@ -168,6 +174,7 @@ var fromBalanceBefore, txInfo = {}; function testSave(testInput, testExpect, done) { + console.log("==> testSave"); var call = { "function": testInput.func, @@ -191,7 +198,8 @@ function testSave(testInput, testExpect, done) { }); return neb.api.estimateGas(from.getAddressString(), contractAddr, - testInput.value, txInfo.nonce, 0, testInput.gasLimit, txInfo.call); + testInput.value, txInfo.nonce, testInput.gasPrice, testInput.gasLimit, txInfo.call); + }).then(function(resp){ expect(resp).to.have.property('gas'); gasUsed = resp.gas; @@ -259,6 +267,7 @@ function testSave(testInput, testExpect, done) { } function testTakeout(testInput, testExpect, done) { + console.log("==>testTakeout"); neb.api.getAccountState(contractAddr).then(function(state){ console.log("[before take] contract state: " + JSON.stringify(state)); contractBalanceBefore = new BigNumber(state.balance); @@ -282,7 +291,7 @@ function testTakeout(testInput, testExpect, done) { tx.signTransaction(); neb.api.estimateGas(from.getAddressString(), contractAddr, - testInput.value, parseInt(state.nonce) + 1, 0, testInput.gasLimit, call).then(function(resp){ + testInput.value, parseInt(state.nonce) + 1, testInput.gasPrice, testInput.gasLimit, call).then(function(resp){ expect(resp).to.have.property('gas'); gasUsed = resp.gas; }).catch(function(err) { @@ -293,67 +302,73 @@ function testTakeout(testInput, testExpect, done) { }).then(function(resp){ expect(resp).to.have.property('txhash'); checkTransaction(resp.txhash, function(receipt){ + try { + console.log("22: receipt", receipt); + if (testExpect.canExecuteTx) { + expect(receipt).to.have.property('status').equal(1); + } else { + expect(receipt).to.have.property('status').equal(0); + } - if (testExpect.canExecuteTx) { - expect(receipt).to.have.property('status').equal(1); - } else { - expect(receipt).to.not.have.property('status'); - } - - neb.api.getAccountState(receipt.from).then(function(state){ + console.log("333"); + neb.api.getAccountState(receipt.from).then(function (state) { - // from balance change - fromBalanceChange = new BigNumber(state.balance).sub(new BigNumber(fromBalanceBefore)); - console.log("[after take] from state: " + JSON.stringify(state) + ", balance change: " + fromBalanceChange); + // from balance change + fromBalanceChange = new BigNumber(state.balance).sub(new BigNumber(fromBalanceBefore)); + console.log("[after take] from state: " + JSON.stringify(state) + ", balance change: " + fromBalanceChange); - return neb.api.getAccountState(receipt.to); - }).then(function(cstate){ - + return neb.api.getAccountState(receipt.to); + }).then(function (cstate) { - return neb.api.getAccountState(coinbase).then(function(state){ - - var coinbalancechange = new BigNumber(state.balance).sub(new BigNumber(coinBalanceBefore)) - .mod(new BigNumber(1.4269).mul(new BigNumber(10).pow(new BigNumber(18)))); - console.log("[after take] coinbase state:" + JSON.stringify(state) + ", balance change: " + coinbalancechange); + return neb.api.getAccountState(coinbase).then(function (state) { - var chg = contractBalanceBefore.sub(new BigNumber(cstate.balance)); - console.log("[after take] contract state: " + JSON.stringify(cstate) + ", balance change: " + chg); + var coinbalancechange = new BigNumber(state.balance).sub(new BigNumber(coinBalanceBefore)) + .mod(new BigNumber(1.4269).mul(new BigNumber(10).pow(new BigNumber(18)))); + console.log("[after take] coinbase state:" + JSON.stringify(state) + ", balance change: " + coinbalancechange); - if (testExpect.canExecuteTx) { - var isEqual = chg.equals(new BigNumber(testExpect.takeBalance)); - expect(isEqual).to.be.true; - } - - return neb.api.getEventsByHash(receipt.hash); - - }); - - }).then(function(evtResp){ + var chg = contractBalanceBefore.sub(new BigNumber(cstate.balance)); + console.log("[after take] contract state: " + JSON.stringify(cstate) + ", balance change: " + chg); - if (!testExpect.canExecuteTx) { - expect(evtResp.events[0].topic).to.equal(testExpect.eventTopic); - done(); - } else { - neb.api.gasPrice().then(function(resp){ - expect(resp).to.have.property('gas_price'); - console.log("[after take] gas price:" + resp['gas_price'] + ", gas used: " + gasUsed); - gasPrice = resp['gas_price']; - - var t = new BigNumber(testExpect.takeBalance).sub(new BigNumber(gasUsed) - .mul(new BigNumber(gasPrice))); - var isEqual = fromBalanceChange.equals(t); - - expect(isEqual).to.be.true; - done(); - }).catch(function(err){ - done(err); + if (testExpect.canExecuteTx) { + var isEqual = chg.equals(new BigNumber(testExpect.takeBalance)); + expect(isEqual).to.be.true; + } + + return neb.api.getEventsByHash(receipt.hash); }); - } - }).catch(function(err){ - console.log(err.error); + + }).then(function (evtResp) { + + if (!testExpect.canExecuteTx) { + + expect(evtResp.events[0].topic).to.equal(testExpect.eventTopic); + done(); + + } else { + neb.api.gasPrice().then(function (resp) { + expect(resp).to.have.property('gas_price'); + console.log("[after take] gas price:" + resp['gas_price'] + ", gas used: " + gasUsed); + gasPrice = resp['gas_price']; + + var t = new BigNumber(testExpect.takeBalance).sub(new BigNumber(gasUsed) + .mul(new BigNumber(gasPrice))); + var isEqual = fromBalanceChange.equals(t); + + expect(isEqual).to.be.true; + done(); + }).catch(function (err) { + done(err); + }); + } + }).catch(function (err) { + console.log(err.error); + done(err) + }); + } catch (err) { + console.log(JSON.stringify(err)); done(err) - }); - }); + } + }) }).catch(function(err){ console.log(err.error); if (testExpect.hasError) { @@ -370,6 +385,7 @@ function testTakeout(testInput, testExpect, done) { } function testVerifyAddress(testInput, testExpect, done) { + console.log("==> testVerifyAddress"); neb.api.getAccountState(contractAddr).then(function(state){ console.log("[before verify] contract state: " + JSON.stringify(state)); contractBalanceBefore = new BigNumber(state.balance); @@ -388,7 +404,7 @@ function testVerifyAddress(testInput, testExpect, done) { "function": testInput.func, "args": testInput.args } - return neb.api.call(from.getAddressString(), contractAddr, testInput.value, parseInt(state.nonce) + 1, 0, testInput.gasLimit, call); + return neb.api.call(from.getAddressString(), contractAddr, testInput.value, parseInt(state.nonce) + 1, testInput.gasPrice, testInput.gasLimit, call); }).then(resp => { console.log("response: " + JSON.stringify(resp)); expect(resp).to.have.property('result') @@ -398,6 +414,7 @@ function testVerifyAddress(testInput, testExpect, done) { } function claimNas(contractType, done) { + console.log("==> claimNas"); from = Account.NewAccount(); console.log("from addr:" + from.getAddressString()); @@ -419,7 +436,8 @@ function claimNas(contractType, done) { if (redeploy) { var testInput = { contractType: contractType, - gasLimit: 2000000 + gasLimit: 2000000, + gasPrice: 1000000, }; redeploy = false; deployContract(testInput, done); @@ -444,14 +462,16 @@ describe('bankvault.' + scriptType, function() { it('save before take', function(done){ var testInput = { gasLimit: 2000000, + gasPrice: 1000000, + func: "save", args: "[0]", value: 20000000000, - } + }; var testExpect = { canExecuteTx: true - } + }; testSave(testInput, testExpect, done); }); @@ -461,6 +481,8 @@ describe('bankvault.' + scriptType, function() { // take var testInput = { gasLimit: 2000000, + gasPrice: 1000000, + func: "takeout", args: "[10000000000]", value: "0" //no use @@ -483,6 +505,8 @@ describe('bankvault.' + scriptType, function() { it('save before take', function(done){ var testInput = { gasLimit: 2000000, + gasPrice: 1000000, + func: "save", args: "[0]", value: 20000000000, @@ -499,6 +523,7 @@ describe('bankvault.' + scriptType, function() { // take var testInput = { gasLimit: 2000000, + gasPrice: 1000000, func: "takeout", args: "[40000000000]", value: "0" //no use @@ -507,7 +532,8 @@ describe('bankvault.' + scriptType, function() { var testExpect = { canExecuteTx: false, // actually, should be `false` takeBalance: '40000000000', // same with testInput.args[0] - eventTopic: 'chain.executeTxFailed', + // eventTopic: 'chain.executeTxFailed', + eventTopic: 'chain.transactionResult', hasError: true, errorMsg: 'execution failed' } @@ -519,6 +545,8 @@ describe('bankvault.' + scriptType, function() { // take var testInput = { gasLimit: 2000000, + gasPrice: 1000000, + func: "takeout", args: "[0]", value: "0" //no use @@ -536,6 +564,8 @@ describe('bankvault.' + scriptType, function() { // take var testInput = { gasLimit: 2000000, + gasPrice: 1000000, + func: "takeout", args: "[-40000000000]", value: "0" //no use @@ -544,7 +574,8 @@ describe('bankvault.' + scriptType, function() { var testExpect = { canExecuteTx: false, // actually, should be `false` takeBalance: '-40000000000', // same with testInput.args[0] - eventTopic: 'chain.executeTxFailed', + // eventTopic: 'chain.executeTxFailed', + eventTopic: 'chain.transactionResult', hasError: true, errorMsg: 'execution failed' } @@ -561,6 +592,8 @@ describe('bankvault.' + scriptType, function() { it('save(40) before take', done => { var testInput = { gasLimit: 2000000, + gasPrice: 1000000, + func: "save", args: "[15]", value: 20000000000, @@ -577,15 +610,18 @@ describe('bankvault.' + scriptType, function() { // take var testInput = { gasLimit: 2000000, + gasPrice: 1000000, + func: "takeout", args: "[10000000000]", value: "0" //no use } var testExpect = { - canExecuteTx: false, // actually, should be `false` + canExecuteTx: false, takeBalance: '10000000000', // same with testInput.args[0] - eventTopic: 'chain.executeTxFailed' + // eventTopic: 'chain.executeTxFailed' + eventTopic: 'chain.transactionResult' } testTakeout(testInput, testExpect, done); @@ -595,19 +631,21 @@ describe('bankvault.' + scriptType, function() { // take var testInput = { gasLimit: 2000000, + gasPrice: 1000000, + func: "takeout", args: "[10000000000]", value: "0" //no use } var testExpect = { - canExecuteTx: true, // actually, should be `false` + canExecuteTx: true, takeBalance: '10000000000' // same with testInput.args[0] } setTimeout(() => { testTakeout(testInput, testExpect, done); - }, 25 * 5 * 1000); + }, 25 * blockInterval * 1000); }); }); @@ -622,6 +660,8 @@ describe('bankvault.' + scriptType, function() { // take var testInput = { gasLimit: 2000000, + gasPrice: 1000000, + func: "takeout", args: "[10000000000]", value: "0" //no use @@ -630,7 +670,8 @@ describe('bankvault.' + scriptType, function() { var testExpect = { canExecuteTx: false , takeBalance: '0', - eventTopic: 'chain.executeTxFailed' + // eventTopic: 'chain.executeTxFailed' + eventTopic: 'chain.transactionResult' } testTakeout(testInput, testExpect, done); @@ -645,6 +686,8 @@ describe('bankvault.' + scriptType, function() { it('5-1. save non-negative value', function(done){ var testInput = { gasLimit: 2000000, + gasPrice: 1000000, + func: "save", args: "[0]", value: 10000000000, @@ -660,6 +703,8 @@ describe('bankvault.' + scriptType, function() { it('5-2. save negative value', function(done){ var testInput = { gasLimit: 2000000, + gasPrice: 1000000, + func: "save", args: "[0]", value: -20000000000, @@ -667,7 +712,7 @@ describe('bankvault.' + scriptType, function() { var testExpect = { hasError: true, - errorMsg: "uint128: underflow" + errorMsg: "invalid value" } testSave(testInput, testExpect, done); @@ -676,6 +721,7 @@ describe('bankvault.' + scriptType, function() { it('5-3. save negative height', done => { var testInput = { gasLimit: 2000000, + gasPrice: 1000000, func: "save", args: "[-500]", value: 20000000000, @@ -699,8 +745,9 @@ describe('bankvault.' + scriptType, function() { var testInput = { value: "0", gasLimit: 2000000, + gasPrice: 1000000, func: "verifyAddress", - args: "[\"d2e558ebf403d10cc435e7ddff5906fcb2c8d033d74cc305\"]" + args: "[\"n1QZMXSZtW7BUerroSms4axNfyBGyFGkrh5\"]" } var testExpect = { @@ -714,6 +761,7 @@ describe('bankvault.' + scriptType, function() { var testInput = { value: "0", gasLimit: 2000000, + gasPrice: 1000000, func: "verifyAddress", args: "[\"slfjlsalfksdflsfjks\"]" } diff --git a/nebtestkit/cases/contract/contract.feature.accept.test.js b/nebtestkit/cases/contract/contract.feature.accept.test.js new file mode 100644 index 000000000..48988d096 --- /dev/null +++ b/nebtestkit/cases/contract/contract.feature.accept.test.js @@ -0,0 +1,455 @@ +'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 testBinary(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); + 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); + } + } + } + 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) { + console.log("send tx err", 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_accept_func_with_args.js", + "type": "js", + "groupname": "case group 0: accept takes some args with execution error", + "groupIndex": 0, + + cases: [ + { + "name": "0-1. value = 0, invalid args", + "testInput": { + // from: from, + // to: contractAddr, + value: "0", + nonce: 1, + gasPrice: 1000000, + gasLimit: 2000000 + }, + "testExpect": { + canExcuteTx: false, + toBalanceChange: "0", + status: 0, + eventErr: "Binary: BigNumber Error: plus() not a number: undefined" + } + } + ] +}; +testCaseGroups.push(caseGroup); + +caseGroup = { + "filename": "contract_accept_func_with_args_2.js", + "type": "js", + "groupname": "case group 1: accept takes some args witchout execution error", + "groupIndex": 1, + + cases: [ + { + "name": "1-1. value = 0", + "testInput": { + // from: from, + // to: contractAddr, + value: "0", + nonce: 1, + gasPrice: 1000000, + gasLimit: 2000000 + }, + "testExpect": { + canExcuteTx: true, + toBalanceChange: "0", + status: 1 + } + }, + + { + "name": "1-2. value > 0", + "testInput": { + // from: from, + // to: contractAddr, + value: "100", + nonce: 1, + gasPrice: 1000000, + gasLimit: 2000000 + }, + "testExpect": { + canExcuteTx: true, + toBalanceChange: "100", + status: 1 + } + } + ] +}; +testCaseGroups.push(caseGroup); + +caseGroup = { + "filename": "bank_vault_contract.js", + "type": "js", + "groupname": "case group 2: bankvault without accept func", + "groupIndex": 2, + + cases: [ + { + "name": "2-1. value > 0", + "testInput": { + // from: from, + // to: contractAddr, + value: "100", + nonce: 1, + gasPrice: 1000000, + gasLimit: 2000000 + }, + "testExpect": { + canExcuteTx: false, + toBalanceChange: "0", + status: 0, + eventErr: "Binary: TypeError: Cannot read property 'apply' of undefined" + } + } + ] +}; +testCaseGroups.push(caseGroup); + +caseGroup = { + "filename": "contract_accept_func_standard.js", + "type": "js", + "groupname": "case group 3: bankvault with standard accept func", + "groupIndex": 3, + + cases: [ + { + "name": "3-1. value > 0", + "testInput": { + // from: from, + // to: contractAddr, + value: "100", + nonce: 1, + gasPrice: 1000000, + gasLimit: 2000000 + }, + "testExpect": { + canExcuteTx: true, + toBalanceChange: "100", + status: 1 + } + } + ] +}; +testCaseGroups.push(caseGroup); + +describe('accept func test', () => { + + before(done => prepareSource(done)); + + for (var i = 0; i < testCaseGroups.length; i++) { + + // if (i != 2) {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)); + testBinary(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.feature.date.test.js b/nebtestkit/cases/contract/contract.feature.date.test.js new file mode 100644 index 000000000..4fba6e3d6 --- /dev/null +++ b/nebtestkit/cases/contract/contract.feature.date.test.js @@ -0,0 +1,400 @@ +'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"); + } + } + 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_random.js", + "type": "js", + "groupname": "case group 0: Date", + "groupIndex": 0, + + cases: [ + { + "name": "0-1. test 'new 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 'new Date('1995-12-17T03:24:00')'", + "testInput": { + value: "0", + nonce: 1, + gasPrice: 1000000, + gasLimit: 2000000, + contract: { + function: "testDate", + args: "[\"1995-12-17T03:24:00\"]" + } + }, + "testExpect": { + canExcuteTx: true, + toBalanceChange: "0", + status: 1, + equalBlockTime: false + } + }, + { + "name": "0-3. test unsupported method", + "testInput": { + value: "0", + nonce: 1, + gasPrice: 1000000, + gasLimit: 2000000, + contract: { + function: "testDate2", + args: "" + } + }, + "testExpect": { + canExcuteTx: false, + toBalanceChange: "0", + status: 0, + eventErr: "Call: Error: Unsupported method!" + } + } + ] +}; +testCaseGroups.push(caseGroup); + +describe('Contract Date 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.feature.random.test.js b/nebtestkit/cases/contract/contract.feature.random.test.js new file mode 100644 index 000000000..9252ed3b8 --- /dev/null +++ b/nebtestkit/cases/contract/contract.feature.random.test.js @@ -0,0 +1,400 @@ +'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.random") { + var result = JSON.parse(event.data); + expect(result.defaultSeedRandom1 == result.userSeedRandom).to.equal(testExpect.equalr1r2); + console.log("check equalr1r2 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_random.js", + "type": "js", + "groupname": "case group 0: Math.random", + "groupIndex": 0, + + cases: [ + { + "name": "0-1. test 'not define user seed'", + "testInput": { + value: "0", + nonce: 1, + gasPrice: 1000000, + gasLimit: 2000000, + contract: { + function: "testRandom", + args: "" + } + }, + "testExpect": { + canExcuteTx: false, + toBalanceChange: "0", + status: 0, + eventErr: "Call: Error: input seed must be a string" + } + }, + { + "name": "0-2. test 'empty user seed('') == reset random seed'", + "testInput": { + value: "0", + nonce: 1, + gasPrice: 1000000, + gasLimit: 2000000, + contract: { + function: "testRandom", + args: "[\"\"]" + } + }, + "testExpect": { + canExcuteTx: true, + toBalanceChange: "0", + status: 1, + equalr1r2: true + } + }, + { + "name": "0-3. test 'set user seed('abc')'", + "testInput": { + value: "0", + nonce: 1, + gasPrice: 1000000, + gasLimit: 2000000, + contract: { + function: "testRandom", + args: "[\"abc\"]" + } + }, + "testExpect": { + canExcuteTx: true, + toBalanceChange: "0", + status: 1, + equalr1r2: false + } + } + ] +}; +testCaseGroups.push(caseGroup); + +describe('Contract Math.random 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/rpc/admin.SignTransactionWithPassphrase.test.js b/nebtestkit/cases/rpc/admin.SignTransactionWithPassphrase.test.js index fa84d4e05..cbb600564 100644 --- a/nebtestkit/cases/rpc/admin.SignTransactionWithPassphrase.test.js +++ b/nebtestkit/cases/rpc/admin.SignTransactionWithPassphrase.test.js @@ -10,7 +10,7 @@ var coinbase, server_address; var env = process.env.NET || 'local'; -var env = 'maintest'; +var env = 'local'; if (env === 'testneb1') { chain_id = 1001; sourceAccount = new Wallet.Account("25a3a441a34658e7a595a0eda222fa43ac51bd223017d17b420674fb6d0a4d52"); @@ -450,11 +450,186 @@ describe("rpc: SignTransaction with passphrase", () => { } var testExpect = { - hasError: false, - errorMsg: "", + hasError: true, + errorMsg: "invalid contract", } testSignTransaction(testInput, testExpect, done) }); + +it('17. `invalid type`', done => { + var testInput = { + transaction: { + from: address, + to: coinbase, + value: "123", + nonce: "10000", + gas_price: "1000000", + gas_limit: "1000000", + contract: { + "function": "save", + }, + type: "invalid" + }, + passphrase: 'passphrase' + } + + var testExpect = { + hasError: true, + errorMsg: "invalid transaction data payload type", + + } + + testSignTransaction(testInput, testExpect, done) +}); + +it('18. `binary type`', done => { + var testInput = { + transaction: { + from: address, + to: coinbase, + value: "123", + nonce: "10000", + gas_price: "1000000", + gas_limit: "1000000", + type: "binary" + }, + passphrase: 'passphrase' + } + + var testExpect = { + hasError: false, + errorMsg: "", + + } + + testSignTransaction(testInput, testExpect, done) +}); + +it('19. `deploy type`', done => { + var testInput = { + transaction: { + from: address, + to: coinbase, + value: "123", + nonce: "10000", + gas_price: "1000000", + gas_limit: "1000000", + type: "deploy", + contract: { + "source": "var a = {}", + "source_type": "ts" + } + }, + passphrase: 'passphrase' + } + + var testExpect = { + hasError: false, + errorMsg: "", + + } + + testSignTransaction(testInput, testExpect, done) +}); + +it('20. `deploy type parse err`', done => { + var testInput = { + transaction: { + from: address, + to: coinbase, + value: "123", + nonce: "10000", + gas_price: "1000000", + gas_limit: "1000000", + type: "deploy", + contract: { + "source": "var a = {}" + } + }, + passphrase: 'passphrase' + } + + var testExpect = { + hasError: true, + errorMsg: "invalid source type of deploy payload", + + } + + testSignTransaction(testInput, testExpect, done) +}); + +it('21. `call type`', done => { + var testInput = { + transaction: { + from: address, + to: coinbase, + value: "123", + nonce: "10000", + gas_price: "1000000", + gas_limit: "1000000", + type: "call", + contract: { + "function": "save" + } + }, + passphrase: 'passphrase' + } + + var testExpect = { + hasError: false, + errorMsg: "", + + } + + testSignTransaction(testInput, testExpect, done) +}); +it('22. `call type function err`', done => { + var testInput = { + transaction: { + from: address, + to: coinbase, + value: "123", + nonce: "10000", + gas_price: "1000000", + gas_limit: "1000000", + type: "call", + contract: { + "function": "_save" + } + }, + passphrase: 'passphrase' + } + + var testExpect = { + hasError: true, + errorMsg: "invalid function of call payload", + + } + + testSignTransaction(testInput, testExpect, done) +}); +it('23. `call type no function`', done => { + var testInput = { + transaction: { + from: address, + to: coinbase, + value: "123", + nonce: "10000", + gas_price: "1000000", + gas_limit: "1000000", + type: "call" + }, + passphrase: 'passphrase' + } + + var testExpect = { + hasError: true, + errorMsg: "invalid function of call payload", + + } + + testSignTransaction(testInput, testExpect, done) +}); }); \ No newline at end of file diff --git a/nebtestkit/cases/rpc/rpc_client/rpc.proto b/nebtestkit/cases/rpc/rpc_client/rpc.proto index 52764eb0f..85bd138d3 100644 --- a/nebtestkit/cases/rpc/rpc_client/rpc.proto +++ b/nebtestkit/cases/rpc/rpc_client/rpc.proto @@ -342,6 +342,9 @@ message TransactionRequest { // binary data for transaction bytes binary = 10; + + // transaction payload type, enum:binary, deploy, call + string type = 20; } message ContractRequest { diff --git a/nebtestkit/cases/stress/contract.date.and.random.test.js b/nebtestkit/cases/stress/contract.date.and.random.test.js new file mode 100644 index 000000000..5462f40f2 --- /dev/null +++ b/nebtestkit/cases/stress/contract.date.and.random.test.js @@ -0,0 +1,277 @@ +'use strict'; + +var Wallet = require('nebulas'); +var sleep = require("system-sleep"); +var HttpRequest = require("../../node-request"); + +var args = process.argv.splice(2); + +if (args.length != 3) { + console.log("please input args 0:env(local,testneb1,testneb2,testneb3) 1:address number(concurrency) 2:sendtimes"); + return; +} + +var env = args[0]; // local testneb1 testneb2 + +const AddressNumber = parseInt(args[1]); +const SendTimes = parseInt(args[2]); + +if (AddressNumber <= 0 || SendTimes <= 0) { + + console.log("please input correct AddressNumber and SendTimes"); + return; +} + +var Neb = Wallet.Neb; +var neb = new Neb(); + +var ChainID; +var from; + +//local +if (env == 'local') { + neb.setRequest(new HttpRequest("http://127.0.0.1:8685")); //https://testnet.nebulas.io + ChainID = 100; + from = new Wallet.Account("a6e5eb290e1438fce79f5cb8774a72621637c2c9654c8b2525ed1d7e4e73653f"); +} else if (env == 'testneb1') { + neb.setRequest(new HttpRequest("http://35.182.48.19:8685")); + ChainID = 1001; + from = new Wallet.Account("43181d58178263837a9a6b08f06379a348a5b362bfab3631ac78d2ac771c5df3"); +} else if (env == "testneb2") { + neb.setRequest(new HttpRequest("http://34.205.26.12:8685")); + ChainID = 1002; + from = new Wallet.Account("25a3a441a34658e7a595a0eda222fa43ac51bd223017d17b420674fb6d0a4d52"); +} else if (env == "testneb3") { + neb.setRequest(new HttpRequest("http://35.177.214.138:8685")); + ChainID = 1003; + from = new Wallet.Account("43181d58178263837a9a6b08f06379a348a5b362bfab3631ac78d2ac771c5df3"); +} else { + console.log("please input correct env local testneb1 testneb2 testneb3"); + return; +} + +var FS = require("fs"); + +var lastnonce = 0; + +// new account to get address +var accountArray = new Array(); +for (var i = 0; i < AddressNumber; i++) { + var account = Wallet.Account.NewAccount(); + //var hash = account.getAddressString(); + accountArray.push(account); +} + +neb.api.getAccountState(from.getAddressString()).then(function (resp) { + console.log("master accountState resp:", JSON.stringify(resp), ", env: ", env); + lastnonce = parseInt(resp.nonce); + console.log("lastnonce:", lastnonce); +}); + +sleep(2000); + +cliamTokens(); + +var ContractHash; +var ContractAddress; + +var intervalAccount = setInterval(function () { + neb.api.getAccountState(from.getAddressString()).then(function (resp) { + console.log("master accountState resp:" + JSON.stringify(resp)); + var nonce = parseInt(resp.nonce); + console.log("lastnonce:", lastnonce, "resp_nonce:", nonce); + + if (lastnonce <= nonce) { + clearInterval(intervalAccount); + deployContract(); + } + }); +}, 2000); + +function cliamTokens() { + var nonce = lastnonce + 1; + for (var j = 0; j < AddressNumber; j++) { + sendTransaction(nonce, accountArray[j]); + ++nonce; + sleep(30); + } + + lastnonce = nonce - 1; +} + +function deployContract() { + + var nonce = lastnonce; + console.log("nonce:" + nonce); + // create contract + var dateAndRand = FS.readFileSync("../../../nf/nvm/test/contract_date_and_random.js", "utf-8"); + var contract = { + "source": dateAndRand, + "sourceType": "js", + "args": "" + }; + + var transaction = new Wallet.Transaction(ChainID, from, from, "0", ++nonce, "1000000", "20000000000", contract); + transaction.signTransaction(); + var rawTx = transaction.toProtoString(); + + // console.log("contract:" + rawTx); + + neb.api.sendRawTransaction(rawTx).then(function (resp) { + console.log("send raw contract transaction resp:" + JSON.stringify(resp)); + if (resp.balance === "0") { + throw new Error("balance is 0"); + } + ContractHash = resp.txhash; + ContractAddress = resp.contract_address; + + checkContractDeployed(); + }); + + ++lastnonce; +} + +function checkContractDeployed() { + + var retry = 0; + + // contract status and get contract_address + var interval = setInterval(function () { + console.log("getTransactionReceipt hash:" + ContractHash); + neb.api.getTransactionReceipt(ContractHash).then(function (resp) { + + console.log("tx receipt:" + resp.status); + + if (resp.status && resp.status === 1) { + clearInterval(interval); + sendMutilContractTransaction(ContractAddress) + } + }).catch(function (err) { + retry++; + console.log("error!", JSON.stringify(err.error)); + if (retry > 10) { + console.log(JSON.stringify(err.error)); + clearInterval(interval); + } + }); + + }, 2000); +} + + +function sendTransaction(nonce, address) { + var transaction = new Wallet.Transaction(ChainID, from, address, neb.nasToBasic(1), nonce); + transaction.signTransaction(); + var rawTx = transaction.toProtoString(); + neb.api.sendRawTransaction(rawTx).then(function (resp) { + console.log("send raw transaction resp:" + JSON.stringify(resp)); + }); +} + + + +// get current height +var BeginHeight; +// +function sendMutilContractTransaction(address) { + + neb.api.getNebState().then(function (resp) { + BeginHeight = resp.height; + console.log("get NebState resp:" + JSON.stringify(resp)); + }); + + sleep(1000); + var nonce = lastnonce; + var t1 = new Date().getTime(); + for (var j = 0; j < AddressNumber; j++) { + nonce = 0; + sendContractTransaction(0, nonce, accountArray[j], address); + //nonce = nonce + SendTimes; + } + + lastnonce = SendTimes; + sleep(1000 * SendTimes) + getTransactionNumberByHeight(); +} + + + +function sendContractTransaction(sendtimes, nonce, from_address, contract_address) { + if (sendtimes < SendTimes) { + + var X = Math.floor(Math.random() * Math.floor(6)); + + var call = { + "function": X % 2 == 0 ? "testDate" : "testRandom", + "args": "" + } + + console.log("send contract nonce:", nonce, ", call:", JSON.stringify(call)); + var transaction = new Wallet.Transaction(ChainID, from_address, contract_address, "0", ++nonce, "1000000", "2000000000", call); + transaction.signTransaction(); + var rawTx = transaction.toProtoString(); + neb.api.sendRawTransaction(rawTx).then(function (resp) { + console.log("send raw contract transaction resp:" + JSON.stringify(resp)); + sendtimes++; + if (resp.txhash) { + sendContractTransaction(sendtimes, nonce, from_address, contract_address); + } + }); + } +} + +function getTransactionNumberByHeight() { + + var intervalHeight = setInterval(function () { + neb.api.getAccountState(accountArray[0].getAddressString()).then(function (resp) { + console.log("master accountState resp:" + JSON.stringify(resp)); + var nonce = parseInt(resp.nonce); + console.log("lastnonce:", lastnonce, "resp_nonce:", nonce); + + if (lastnonce <= nonce) { + clearInterval(intervalHeight) + sleep(2000) + neb.api.getNebState().then(function (resp) { + var EndHeight = resp.height + console.log("BeginHeight:" + BeginHeight + " EndHeight:" + EndHeight); + var sum = 0; + var max = 0; + var height = BeginHeight + var h = EndHeight - BeginHeight + for (; height <= EndHeight; height++) { + neb.api.getBlockByHeight(height, false).then(function (resp) { + if (resp.transactions) { + //console.log("master accountState resp:" + JSON.stringify(resp)); + console.log(resp.height, resp.transactions.length) + sum += resp.transactions.length + max = resp.transactions.length > max ? resp.transactions.length : max + } else { + console.log(resp.height, 0) + } + --h; + }); + sleep(10) + } + + sleep(1000) + var intervalH = setInterval(function () { + if (h < 0) { + clearInterval(intervalH); + console.log("====================") + console.log("env is ", env) + console.log("concurrency number is ", AddressNumber) + console.log("total number is ", AddressNumber * SendTimes) + console.log("height from ", BeginHeight, " to ", EndHeight) + console.log("max of block is ", max) + console.log("avg of block is ", sum / (EndHeight - BeginHeight)) + console.log("max of tps is ", max / 5) + console.log("avg of tps is ", sum / (5 * (EndHeight - BeginHeight))) + console.log("====================") + } + }, 2000); + + }); + } + }) + }, 1000); +} \ No newline at end of file diff --git a/nebtestkit/cases/stress/stress.test.dag.js b/nebtestkit/cases/stress/stress.test.dag.js index c08c3125b..053bf5cd2 100644 --- a/nebtestkit/cases/stress/stress.test.dag.js +++ b/nebtestkit/cases/stress/stress.test.dag.js @@ -1,37 +1,23 @@ 'use strict'; -var Wallet = require('../../../cmd/console/neb.js/lib/wallet.js'); -var HttpRequest = require("../../node-request"); +var Wallet = require("nebulas"); +//var HttpRequest = Wallet.HttpRequest +var HttpRequest = require("../../node-request.js"); +var utils = Wallet.Utils; var schedule = require('node-schedule'); var sleep = require("system-sleep"); -var env="testneb3"; // local testneb1 testneb2 -var AddressNumber = 100; -var EachAccountSendTimes = 100; -var ChainID = 1003; +var env; // local testneb1 testneb2 +var AddressNumber = 300; +var EachAccountSendTimes = 50; var args = process.argv.splice(2); - -if (args.length != 3) { - // give default config - env = "testneb3"; - AddressNumber = 100; -} else { - env = args[0]; // local testneb1 testneb2 - - AddressNumber = parseInt(args[1]); - EachAccountSendTimes = parseInt(args[2]); -} - -if (AddressNumber <= 0 || EachAccountSendTimes <= 0) { - - console.log("please input correct AddressNumber and SendTimes"); - return; -} +env = args[0]; var Neb = Wallet.Neb; var neb = new Neb(); +var ChainID; var from; var accountArray; // var to = Wallet.Account.NewAccount(); @@ -42,32 +28,141 @@ var startTime; var nodes = new Array(); -neb.setRequest(new HttpRequest("http://35.177.214.138:8685")); -ChainID = 1003; -from = new Wallet.Account("43181d58178263837a9a6b08f06379a348a5b362bfab3631ac78d2ac771c5df3"); -nodes.push("http://35.177.214.138:8685"); +console.log(args); + +//local +if (env === 'local') { + neb.setRequest(new HttpRequest("http://127.0.0.1:8685")); //https://testnet.nebulas.io + ChainID = 100; + from = new Wallet.Account("a6e5eb290e1438fce79f5cb8774a72621637c2c9654c8b2525ed1d7e4e73653f"); + nodes.push("http://127.0.0.1:8685"); +} else if (env === 'testneb1') { + neb.setRequest(new HttpRequest("http://35.182.48.19:8685")); + ChainID = 1001; + from = new Wallet.Account("43181d58178263837a9a6b08f06379a348a5b362bfab3631ac78d2ac771c5df3"); + nodes.push("http://35.182.48.19:8685"); + nodes.push("http://13.57.245.249:8685"); + nodes.push("http://54.219.151.126:8685"); + nodes.push("http://18.218.165.90:8685"); + nodes.push("http://18.219.28.97:8685"); + nodes.push("http://13.58.44.3:8685"); + nodes.push("http://35.177.214.138:8685"); + nodes.push("http://35.176.94.224:8685"); +} else if (env === "testneb2") { + neb.setRequest(new HttpRequest("http://34.205.26.12:8685")); + ChainID = 1002; + from = new Wallet.Account("25a3a441a34658e7a595a0eda222fa43ac51bd223017d17b420674fb6d0a4d52"); + nodes.push("http://34.205.26.12:8685"); + // nodes.push("http://54.206.9.246:8685"); + // nodes.push("http://54.252.158.117:8685"); + // nodes.push("http://34.206.53.244:8685"); + // nodes.push("http://34.205.53.3:8685"); + // nodes.push("http://52.3.226.40:8685"); + +} else if (env === "testneb3") { + + //neb.setRequest(new HttpRequest("http://52.47.199.42:8685")); + neb.setRequest(new HttpRequest("http://35.177.214.138:8685")); + //neb.setRequest(new HttpRequest("http://13.127.227.177:8685")); + ChainID = 1003; + from = new Wallet.Account("25a3a441a34658e7a595a0eda222fa43ac51bd223017d17b420674fb6d0a4d52"); + //nodes.push("http://13.127.227.177:8685"); + nodes.push("http://35.177.214.138:8685"); + //nodes.push("http://52.47.199.42:8685"); + +} else if (env === "testneb4") { + neb.setRequest(new HttpRequest("http://34.208.233.164:8685")); + ChainID = 1004; + from = new Wallet.Account("25a3a441a34658e7a595a0eda222fa43ac51bd223017d17b420674fb6d0a4d52"); + nodes.push("http://34.208.233.164:8685"); + nodes.push("http://54.245.29.152:8685"); + nodes.push("http://52.34.73.0:8685"); + nodes.push("http://54.71.175.99:8685"); + nodes.push("http://34.213.130.120:8685"); + nodes.push("http://18.197.107.228:8685"); + nodes.push("http://18.197.106.150:8685"); + nodes.push("http://54.93.121.146:8685"); + nodes.push("http://18.195.159.210:8685"); + nodes.push("http://18.197.157.46:8685"); + nodes.push("http://18.228.3.118:8685"); + nodes.push("http://18.231.173.99:8685"); + nodes.push("http://18.231.124.140:8685"); + nodes.push("http://18.231.183.193:8685"); + nodes.push("http://18.231.162.23:8685"); + nodes.push("http://34.253.237.122:8685"); + nodes.push("http://34.244.129.30:8685"); + nodes.push("http://54.229.241.235:8685"); + nodes.push("http://54.229.177.109:8685"); + nodes.push("http://34.250.18.201:8685"); + nodes.push("http://13.127.227.177:8685"); + +} else if (env === "liuliang") { + neb.setRequest(new HttpRequest("http://35.154.108.11:8685")); + ChainID = 1001; + from = new Wallet.Account("c75402f6ffe6edcc2c062134b5932151cb39b6486a7beb984792bb9da3f38b9f"); + nodes.push("http://35.154.108.11:8685"); + +} else if (env === "maintest"){ + ChainID = 2; + from = new Wallet.Account("d2319a8a63b1abcb0cc6d4183198e5d7b264d271f97edf0c76cfdb1f2631848c"); + neb.setRequest(new HttpRequest("http://54.149.15.132:8685")); + nodes.push("http://54.149.15.132:8685"); + nodes.push("http://18.188.27.35:8685"); + nodes.push("http://34.201.23.199:8685"); + nodes.push("http://13.251.33.39:8685"); + nodes.push("http://52.56.55.238:8685"); + +} else if (env === "testnet_cal_super") { + neb.setRequest(new HttpRequest("http://13.57.96.40:8685")); + ChainID = 1001; + from = new Wallet.Account("25a3a441a34658e7a595a0eda222fa43ac51bd223017d17b420674fb6d0a4d52"); + // from = new Wallet.Account("d2319a8a63b1abcb0cc6d4183198e5d7b264d271f97edf0c76cfdb1f2631848c"); + + nodes.push("http://52.60.150.236:8685"); + nodes.push("http://13.57.96.40:8685"); + +} else { + console.log("please input correct env local testneb1 testneb2"); + return; +} + + +var count = AddressNumber * EachAccountSendTimes; + +var interval = setInterval(function () { + + if (count >= AddressNumber * EachAccountSendTimes) { + count = 0; + + console.log("sending request to ", env); + neb.api.getAccountState(from.getAddressString()).then(function (resp) { + console.log("master accountState resp:" + JSON.stringify(resp)); + //lastnonce = parseInt(resp.result.nonce); + lastnonce = parseInt(resp.nonce); + console.log("lastnonce:", lastnonce); + + claimTokens(lastnonce); + }); + } + +}, 20000); + + -neb.api.getAccountState(from.getAddressString()).then(function (resp) { - console.log("master accountState resp:" + JSON.stringify(resp)); - lastnonce = parseInt(resp.nonce); - console.log("lastnonce:", lastnonce); - claimTokens(lastnonce); -}); function claimTokens(nonce) { console.log("initializing " + AddressNumber + " accounts with coins !!!") + console.log(from.getAddressString()); + accountArray = new Array(); for (var i = 0; i < AddressNumber; i++) { var account = Wallet.Account.NewAccount(); accountArray.push(account); - sendTransaction(0, 1, from, account, "1000000000000000", ++nonce); - - sleep(10); + sleep(5); } - checkClaimTokens(); } @@ -79,19 +174,33 @@ function sendTransaction(index, totalTimes, from, to, value, nonce, randomToAddr to = accountArray[randomTo]; } - var transaction = new Wallet.Transaction(1003, from, to, value, nonce); + var transaction = new Wallet.Transaction(ChainID, from, to, value, nonce); transaction.signTransaction(); var rawTx = transaction.toProtoString(); + var i = Math.floor((Math.random() * nodes.length)); + var node = nodes[i]; + neb.setRequest(new HttpRequest(node)); + count ++; neb.api.sendRawTransaction(rawTx).then(function (resp) { - console.log("send raw transaction resp:" + JSON.stringify(resp)); + console.log("send raw tx resp:" + JSON.stringify(resp) + " (" + index + "/" + totalTimes + ")"); + //if (resp.result.txhash) { if (resp.txhash) { if (nonce % 10 === 0){ - sleep(10); + sleep(20); } sendTransaction(++index, totalTimes, from, to, value, ++nonce, randomToAddr); } }); + + // .catch(function (err) { + // console.log(err); + // console.log(JSON.stringify(err)); + // console.log("send tx error, retry: " + "from:" + from.getAddressString() + " tx_index: (" + index + "/" + totalTimes + ")" + " node:" + node); + // sleep(10); + // sendTransaction(index, totalTimes, from, to, value, nonce, randomToAddr); + // } + // ); } } @@ -99,9 +208,9 @@ function checkClaimTokens() { var interval = setInterval(function () { neb.api.getAccountState(from.getAddressString()).then(function (resp) { console.log("master accountState resp:" + JSON.stringify(resp)); + //if (resp.result.nonce >= lastnonce + AddressNumber) { if (resp.nonce >= lastnonce + AddressNumber) { clearInterval(interval); - sendTransactionsForTps(); } }); @@ -115,11 +224,10 @@ function sendTransactionsForTps() { startTime = new Date().getTime(); for (var i = 0; i < AddressNumber; i++) { - var node = nodes[i % nodes.length]; neb.setRequest(new HttpRequest(node)); - var randomValue = Math.floor((Math.random() * 10)); + var randomValue = Math.floor((Math.random() * 100)); sendTransaction(0, EachAccountSendTimes, accountArray[i], null, randomValue, 1, true /*random to addr*/); - sleep(10); + sleep(5); } } diff --git a/nebtestkit/cases/testnet_config.js b/nebtestkit/cases/testnet_config.js index 09fafb10e..f53559abc 100644 --- a/nebtestkit/cases/testnet_config.js +++ b/nebtestkit/cases/testnet_config.js @@ -59,7 +59,7 @@ var TestNet = function (env) { // throw new Error("invalid env (" + env + ")."); this.ChainId = 100; - this.sourceAccount = new Wallet.Account("d80f115bdbba5ef215707a8d7053c16f4e65588fd50b0f83369ad142b99891b5"); + this.sourceAccount = new Wallet.Account("1d3fe06a53919e728315e2ccca41d4aa5b190845a79007797517e62dbc0df454"); this.coinbase = "n1QZMXSZtW7BUerroSms4axNfyBGyFGkrh5"; this.apiEndPoint = "http://127.0.0.1:8685"; diff --git a/nebtestkit/package.json b/nebtestkit/package.json index 9059f495a..2a6c52174 100644 --- a/nebtestkit/package.json +++ b/nebtestkit/package.json @@ -18,13 +18,13 @@ "grpc": "^1.10.0", "lodash": "^4.17.5", "minimist": "^1.2.0", - "nebulas": "^0.3.7", + "nebulas": "^0.4.6", "node-schedule": "^1.3.0", "protobufjs": "^6.8.6", + "request": "^2.85.0", + "request-promise": "^4.2.2", "shelljs": "^0.7.8", "sync-request": "^4.1.0", - "system-sleep": "^1.3.6", - "request": "^2.85.0", - "request-promise": "^4.2.2" + "system-sleep": "^1.3.6" } } diff --git a/net/dispatcher.go b/net/dispatcher.go index 2f16b6559..b5d4c94c4 100644 --- a/net/dispatcher.go +++ b/net/dispatcher.go @@ -102,6 +102,9 @@ func (dp *Dispatcher) loop() { msgType := msg.MessageType() v, _ := dp.subscribersMap.Load(msgType) + if v == nil { + continue + } m, _ := v.(*sync.Map) m.Range(func(key, value interface{}) bool { diff --git a/net/neb_message.go b/net/neb_message.go index 66173089a..f566cdf3f 100644 --- a/net/neb_message.go +++ b/net/neb_message.go @@ -76,12 +76,17 @@ const ( // Consider that a block is too large in sync. MaxNebMessageDataLength = 512 * 1024 * 1024 // 512m. MaxNebMessageNameLength = 24 - 12 // 12. + + DefaultReservedFlag = 0x0 + ReservedCompressionEnableFlag = 0x80 + ReservedCompressionClientFlag = 0x1 ) // Error types var ( MagicNumber = []byte{0x4e, 0x45, 0x42, 0x31} - DefaultReserved = []byte{0x80, 0x0, 0x0} + DefaultReserved = []byte{DefaultReservedFlag, DefaultReservedFlag, DefaultReservedFlag} + CurrentReserved = []byte{DefaultReservedFlag | ReservedCompressionEnableFlag, DefaultReservedFlag, DefaultReservedFlag} ErrInsufficientMessageHeaderLength = errors.New("insufficient message header length") ErrInsufficientMessageDataLength = errors.New("insufficient message data length") @@ -90,6 +95,7 @@ var ( ErrInvalidDataCheckSum = errors.New("invalid data checksum") ErrExceedMaxDataLength = errors.New("exceed max data length") ErrExceedMaxMessageNameLength = errors.New("exceed max message name length") + ErrUncompressMessageFailed = errors.New("uncompress message failed") ) //NebMessage struct @@ -157,7 +163,21 @@ func (message *NebMessage) HeaderWithoutCheckSum() []byte { } // Data return data -func (message *NebMessage) Data() []byte { +func (message *NebMessage) Data() ([]byte, error) { + reserved := message.Reserved() + data := message.content[NebMessageHeaderLength:] + if (reserved[0] & ReservedCompressionEnableFlag) > 0 { + var err error + data, err = snappy.Decode(nil, data) + if err != nil { + return nil, ErrUncompressMessageFailed + } + } + return data, nil +} + +// OriginalData return original data +func (message *NebMessage) OriginalData() []byte { return message.content[NebMessageHeaderLength:] } @@ -172,15 +192,10 @@ func (message *NebMessage) Length() uint64 { } // NewNebMessage new neb message -func NewNebMessage(s *Stream, reserved []byte, version byte, messageName string, data []byte) (*NebMessage, error) { - chainID := s.node.config.ChainID - // if remote peer version >= compress version, compress message data. - if messageName != HELLO { - if v, ok := s.compressFlag.Load(s.pid.Pretty()); ok { - if (v.(byte) & 0x80) > 0 { - data = snappy.Encode(nil, data) - } - } +func NewNebMessage(chainID uint32, reserved []byte, version byte, messageName string, data []byte) (*NebMessage, error) { + // Process message compression + if (reserved[2] != ReservedCompressionClientFlag) && ((reserved[0] & ReservedCompressionEnableFlag) > 0) { + data = snappy.Encode(nil, data) } if len(data) > MaxNebMessageDataLength { @@ -199,7 +214,6 @@ func NewNebMessage(s *Stream, reserved []byte, version byte, messageName string, "limits": MaxNebMessageNameLength, }).Debug("Exceeded max message name length.") return nil, ErrExceedMaxMessageNameLength - } dataCheckSum := crc32.ChecksumIEEE(data) @@ -291,7 +305,7 @@ func (message *NebMessage) VerifyHeader() error { // VerifyData verify message data func (message *NebMessage) VerifyData() error { - expectedCheckSum := crc32.ChecksumIEEE(message.Data()) + expectedCheckSum := crc32.ChecksumIEEE(message.OriginalData()) if expectedCheckSum != message.DataCheckSum() { logging.VLog().WithFields(logrus.Fields{ "expect": expectedCheckSum, diff --git a/net/stream.go b/net/stream.go index 758d4a022..83b35420e 100644 --- a/net/stream.go +++ b/net/stream.go @@ -21,12 +21,10 @@ package net import ( "errors" "fmt" - "hash/crc32" + "strings" "sync" "time" - "github.com/golang/snappy" - "github.com/gogo/protobuf/proto" libnet "github.com/libp2p/go-libp2p-net" peer "github.com/libp2p/go-libp2p-peer" @@ -38,14 +36,15 @@ import ( // Stream Message Type const ( - ClientVersion = "0.3.0" - NebProtocolID = "/neb/1.0.0" - HELLO = "hello" - OK = "ok" - BYE = "bye" - SYNCROUTE = "syncroute" - ROUTETABLE = "routetable" - RECVEDMSG = "recvedmsg" + ClientVersion = "0.3.0" + NebProtocolID = "/neb/1.0.0" + HELLO = "hello" + OK = "ok" + BYE = "bye" + SYNCROUTE = "syncroute" + ROUTETABLE = "routetable" + RECVEDMSG = "recvedmsg" + CurrentVersion = 0x0 ) // Stream Status @@ -59,7 +58,6 @@ const ( var ( ErrShouldCloseConnectionAndExitLoop = errors.New("should close connection and exit loop") ErrStreamIsNotConnected = errors.New("stream is not connected") - ErrUncompressMessageFailed = errors.New("uncompress message failed") ) // Stream define the structure of a stream in p2p network @@ -80,7 +78,7 @@ type Stream struct { latestReadAt int64 latestWriteAt int64 msgCount map[string]int - compressFlag *sync.Map + reservedFlag []byte } // NewStream return a new Stream @@ -110,7 +108,7 @@ func newStreamInstance(pid peer.ID, addr ma.Multiaddr, stream libnet.Stream, nod latestReadAt: 0, latestWriteAt: 0, msgCount: make(map[string]int), - compressFlag: new(sync.Map), + reservedFlag: DefaultReserved, } } @@ -175,7 +173,7 @@ func (s *Stream) SendProtoMessage(messageName string, pb proto.Message, priority // SendMessage send msg to buffer func (s *Stream) SendMessage(messageName string, data []byte, priority int) error { - message, err := NewNebMessage(s, DefaultReserved, 0, messageName, data) + message, err := NewNebMessage(s.node.config.ChainID, s.reservedFlag, CurrentVersion, messageName, data) if err != nil { return err } @@ -264,7 +262,7 @@ func (s *Stream) WriteNebMessage(message *NebMessage) error { } // WriteProtoMessage write proto msg in the stream -func (s *Stream) WriteProtoMessage(messageName string, pb proto.Message) error { +func (s *Stream) WriteProtoMessage(messageName string, pb proto.Message, reservedClientFlag byte) error { data, err := proto.Marshal(pb) if err != nil { logging.VLog().WithFields(logrus.Fields{ @@ -275,12 +273,20 @@ func (s *Stream) WriteProtoMessage(messageName string, pb proto.Message) error { return err } - return s.WriteMessage(messageName, data) + return s.WriteMessage(messageName, data, reservedClientFlag) } // WriteMessage write raw msg in the stream -func (s *Stream) WriteMessage(messageName string, data []byte) error { - message, err := NewNebMessage(s, DefaultReserved, 0, messageName, data) +func (s *Stream) WriteMessage(messageName string, data []byte, reservedClientFlag byte) error { + // hello and ok messages come with the client flag bit. + var reserved = make([]byte, len(s.reservedFlag)) + copy(reserved, s.reservedFlag) + + if reservedClientFlag == ReservedCompressionClientFlag { + reserved[2] = s.reservedFlag[2] | reservedClientFlag + } + + message, err := NewNebMessage(s.node.config.ChainID, reserved, CurrentVersion, messageName, data) if err != nil { return err } @@ -443,28 +449,13 @@ func (s *Stream) writeLoop() { func (s *Stream) handleMessage(message *NebMessage) error { messageName := message.MessageName() - compressFlag := message.Reserved()[0] & 0x80 - s.compressFlag.Store(s.pid.Pretty(), compressFlag) s.msgCount[messageName]++ - // Network data compression compatible with old clients. - // uncompress message data. - var data = message.Data() - if messageName != HELLO { - if compressFlag > 0 { - var err error - data, err = snappy.Decode(nil, message.Data()) - if err != nil { - return ErrUncompressMessageFailed - } - } - } - switch messageName { case HELLO: - return s.onHello(message, data) + return s.onHello(message) case OK: - return s.onOk(message, data) + return s.onOk(message) case BYE: return s.onBye(message) } @@ -478,12 +469,19 @@ func (s *Stream) handleMessage(message *NebMessage) error { case SYNCROUTE: return s.onSyncRoute(message) case ROUTETABLE: - return s.onRouteTable(message, data) + return s.onRouteTable(message) default: + data, err := s.getData(message) + if err != nil { + logging.VLog().WithFields(logrus.Fields{ + "err": err, + "messageName": message.MessageName(), + }).Info("Handle message data occurs error.") + return err + } s.node.netService.PutMessage(NewBaseMessage(message.MessageName(), s.pid.Pretty(), data)) // record recv message. - dataCheckSum := crc32.ChecksumIEEE(data) - RecordRecvMessage(s, dataCheckSum) + RecordRecvMessage(s, message.DataCheckSum()) } return nil @@ -520,7 +518,7 @@ func (s *Stream) close(reason error) { // Bye say bye in the stream func (s *Stream) Bye() { - s.WriteMessage(BYE, []byte{}) + s.WriteMessage(BYE, []byte{}, DefaultReservedFlag) s.close(errors.New("bye: force close")) } @@ -537,11 +535,11 @@ func (s *Stream) Hello() error { NodeId: s.node.id.String(), ClientVersion: ClientVersion, } - return s.WriteProtoMessage(HELLO, msg) + return s.WriteProtoMessage(HELLO, msg, ReservedCompressionClientFlag) } -func (s *Stream) onHello(message *NebMessage, data []byte) error { - msg, err := netpb.HelloMessageFromProto(data) +func (s *Stream) onHello(message *NebMessage) error { + msg, err := netpb.HelloMessageFromProto(message.OriginalData()) if err != nil { return ErrShouldCloseConnectionAndExitLoop } @@ -557,6 +555,10 @@ func (s *Stream) onHello(message *NebMessage, data []byte) error { return ErrShouldCloseConnectionAndExitLoop } + if (message.Reserved()[2] & ReservedCompressionClientFlag) > 0 { + s.reservedFlag = CurrentReserved + } + // add to route table. s.node.routeTable.AddPeerStream(s) @@ -574,11 +576,11 @@ func (s *Stream) Ok() error { ClientVersion: ClientVersion, } - return s.WriteProtoMessage(OK, resp) + return s.WriteProtoMessage(OK, resp, ReservedCompressionClientFlag) } -func (s *Stream) onOk(message *NebMessage, data []byte) error { - msg, err := netpb.OKMessageFromProto(data) +func (s *Stream) onOk(message *NebMessage) error { + msg, err := netpb.OKMessageFromProto(message.OriginalData()) if err != nil { return ErrShouldCloseConnectionAndExitLoop } @@ -594,6 +596,10 @@ func (s *Stream) onOk(message *NebMessage, data []byte) error { return ErrShouldCloseConnectionAndExitLoop } + if (message.Reserved()[2] & ReservedCompressionClientFlag) > 0 { + s.reservedFlag = CurrentReserved + } + // add to route table. s.node.routeTable.AddPeerStream(s) @@ -641,7 +647,12 @@ func (s *Stream) RouteTable() error { return s.SendProtoMessage(ROUTETABLE, msg, MessagePriorityHigh) } -func (s *Stream) onRouteTable(message *NebMessage, data []byte) error { +func (s *Stream) onRouteTable(message *NebMessage) error { + data, err := s.getData(message) + if err != nil { + return err + } + peers := new(netpb.Peers) if err := proto.Unmarshal(data, peers); err != nil { logging.VLog().WithFields(logrus.Fields{ @@ -664,7 +675,52 @@ func (s *Stream) finishHandshake() { s.handshakeSucceedCh <- true } +func (s *Stream) getData(message *NebMessage) ([]byte, error) { + var data []byte + if ByteSliceEqualBCE(s.reservedFlag, CurrentReserved) { + var err error + data, err = message.Data() + if err != nil { + return nil, err + } + } else { + data = message.OriginalData() + } + return data, nil +} + // CheckClientVersionCompatibility if two clients are compatible +// If the clientVersion of node A is X.Y.Z, then node B must be X.Y.{} to be compatible with A. func CheckClientVersionCompatibility(v1, v2 string) bool { - return v1 == v2 + s1 := strings.Split(v1, ".") + s2 := strings.Split(v1, ".") + + if len(s1) != 3 || len(s2) != 3 { + return false + } + + if s1[0] != s2[0] || s1[1] != s2[1] { + return false + } + return true +} + +// ByteSliceEqualBCE determines whether two byte arrays are equal. +func ByteSliceEqualBCE(a, b []byte) bool { + if len(a) != len(b) { + return false + } + + if (a == nil) != (b == nil) { + return false + } + + b = b[:len(a)] + for i, v := range a { + if v != b[i] { + return false + } + } + + return true } diff --git a/net/testing/test/main.go b/net/testing/test/main.go deleted file mode 100644 index f53cbf100..000000000 --- a/net/testing/test/main.go +++ /dev/null @@ -1,47 +0,0 @@ -package main - -import ( - "encoding/json" - "fmt" - "io/ioutil" - "net/http" - "strings" -) - -func main() { - resp, err := http.Get("https://api.github.com/repos/btcsuite/btcd/commits?client_id=80386779008eea5dab41&client_secret=f2086aebf790729026fb209b803010029821d8a3") - - if err != nil { - // handle error - } - defer resp.Body.Close() - body, err := ioutil.ReadAll(resp.Body) - - if err != nil { - // handle error - } - var f interface{} - json.Unmarshal(body, &f) - - for _, n := range f.([]interface{}) { - for k, v := range n.(map[string]interface{}) { - if k == "commit" { - value := v.(map[string]interface{})["message"].(string) - fmt.Println(value) - if strings.Contains(value, "gx publish ") { - url := v.(map[string]interface{})["url"].(string) - fmt.Println(v.(map[string]interface{})["message"]) - temps := strings.Split(url, "/") - fmt.Println(temps[len(temps)-1]) - - str := "github.com/agl/ed25519" - temps = strings.Split(str, "ghub.com") - fmt.Println(len(temps)) - fmt.Println(temps) - return - } - } - } - } - -} diff --git a/nf/nvm/blockchain.go b/nf/nvm/blockchain.go index 31e2f5baf..ce93099e0 100644 --- a/nf/nvm/blockchain.go +++ b/nf/nvm/blockchain.go @@ -26,6 +26,7 @@ import ( "encoding/json" "github.com/nebulasio/go-nebulas/core" + "github.com/nebulasio/go-nebulas/core/state" "github.com/nebulasio/go-nebulas/util" "github.com/nebulasio/go-nebulas/util/byteutils" "github.com/nebulasio/go-nebulas/util/logging" @@ -180,6 +181,37 @@ func TransferFunc(handler unsafe.Pointer, to *C.char, v *C.char, gasCnt *C.size_ } } + 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 TransferRecordEventFailed + } + + 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)}) + } + return TransferFuncSuccess } diff --git a/nf/nvm/context.go b/nf/nvm/context.go index e51d501d1..89136a3c0 100644 --- a/nf/nvm/context.go +++ b/nf/nvm/context.go @@ -35,6 +35,7 @@ type SerializableBlock struct { Timestamp int64 `json:"timestamp"` Hash string `json:"hash"` Height uint64 `json:"height"` + Seed string `json:"seed,omitempty"` } // SerializableTransaction serializable transaction @@ -85,6 +86,9 @@ func toSerializableBlock(block Block) *SerializableBlock { Hash: "", Height: block.Height(), } + if block.RandomAvailable() { + sBlock.Seed = block.RandomSeed() + } return sBlock } diff --git a/nf/nvm/engine_v8.go b/nf/nvm/engine_v8.go index ba524ad6e..20dbd6aae 100644 --- a/nf/nvm/engine_v8.go +++ b/nf/nvm/engine_v8.go @@ -449,7 +449,6 @@ func (e *V8Engine) prepareRunnableContractScript(source, function, args string) } runnableSource = fmt.Sprintf(`Blockchain.blockParse("%s"); Blockchain.transactionParse("%s"); - Object.freeze(Blockchain); var __contract = require("%s"); var __instance = new __contract(); __instance["%s"].apply(__instance, JSON.parse("%s"));`, diff --git a/nf/nvm/engine_v8_test.go b/nf/nvm/engine_v8_test.go index d96381ca4..f1b9e556e 100644 --- a/nf/nvm/engine_v8_test.go +++ b/nf/nvm/engine_v8_test.go @@ -28,8 +28,15 @@ import ( "strings" "sync" "testing" + "time" + + "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" "encoding/json" @@ -69,6 +76,21 @@ func (block *testBlock) Height() uint64 { return 1 } +// RandomSeed mock +func (block *testBlock) RandomSeed() string { + return "59fc526072b09af8a8ca9732dae17132c4e9127e43cf2232" +} + +// RandomAvailable mock +func (block *testBlock) RandomAvailable() bool { + return true +} + +// DateAvailable +func (block *testBlock) DateAvailable() bool { + return true +} + // GetTransaction mock func (block *testBlock) GetTransaction(hash byteutils.Hash) (*core.Transaction, error) { return nil, nil @@ -123,9 +145,11 @@ func TestRunScriptSource(t *testing.T) { {"test/test_storage_class.js", nil, "\"\""}, {"test/test_storage.js", nil, "\"\""}, {"test/test_eval.js", core.ErrExecutionFailed, "EvalError: Code generation from strings disallowed for this context"}, - {"test/test_date.js", core.ErrExecutionFailed, "TypeError: Date.now is not a function"}, + {"test/test_date.js", nil, "\"\""}, {"test/test_bignumber_random.js", core.ErrExecutionFailed, "Error: BigNumber.random is not allowed in nvm."}, - {"test/test_random.js", core.ErrExecutionFailed, "Error: Math.random func is not allowed in nvm."}, + {"test/test_random_enable.js", nil, "\"\""}, + {"test/test_random_disable.js", core.ErrExecutionFailed, "Error: Math.random func is not allowed in nvm."}, + {"test/test_random_seed.js", core.ErrExecutionFailed, "Error: input seed must be a string"}, } for _, tt := range tests { @@ -1229,3 +1253,342 @@ func TestRequireModule(t *testing.T) { }) } } + +type Neb struct { + config *nebletpb.Config + chain *core.BlockChain + ns net.Service + am *account.Manager + genesis *corepb.Genesis + storage storage.Storage + consensus core.Consensus + emitter *core.EventEmitter + nvm core.NVM +} + +func mockNeb(t *testing.T) *Neb { + // storage, _ := storage.NewDiskStorage("test.db") + // storage, err := storage.NewRocksStorage("rocks.db") + // assert.Nil(t, err) + storage, _ := storage.NewMemoryStorage() + eventEmitter := core.NewEventEmitter(1024) + genesisConf := MockGenesisConf() + consensus := dpos.NewDpos() + nvm := NewNebulasVM() + neb := &Neb{ + genesis: genesisConf, + storage: storage, + emitter: eventEmitter, + consensus: consensus, + nvm: nvm, + config: &nebletpb.Config{ + Chain: &nebletpb.ChainConfig{ + ChainId: genesisConf.Meta.ChainId, + Keydir: "keydir", + StartMine: true, + Coinbase: "n1dYu2BXgV3xgUh8LhZu8QDDNr15tz4hVDv", + Miner: "n1FF1nz6tarkDVwWQkMnnwFPuPKUaQTdptE", + Passphrase: "passphrase", + }, + }, + ns: mockNetService{}, + } + + am, _ := account.NewManager(neb) + neb.am = am + + chain, err := core.NewBlockChain(neb) + assert.Nil(t, err) + neb.chain = chain + assert.Nil(t, consensus.Setup(neb)) + assert.Nil(t, chain.Setup(neb)) + + var ns mockNetService + neb.ns = ns + neb.chain.BlockPool().RegisterInNetwork(ns) + + eventEmitter.Start() + return neb +} + +func (n *Neb) Config() *nebletpb.Config { + return n.config +} + +func (n *Neb) BlockChain() *core.BlockChain { + return n.chain +} + +func (n *Neb) NetService() net.Service { + return n.ns +} + +func (n *Neb) IsActiveSyncing() bool { + return true +} + +func (n *Neb) AccountManager() core.AccountManager { + return n.am +} + +func (n *Neb) Genesis() *corepb.Genesis { + return n.genesis +} + +func (n *Neb) Storage() storage.Storage { + return n.storage +} + +func (n *Neb) EventEmitter() *core.EventEmitter { + return n.emitter +} + +func (n *Neb) Consensus() core.Consensus { + return n.consensus +} + +func (n *Neb) Nvm() core.NVM { + return n.nvm +} + +func (n *Neb) StartActiveSync() {} + +func (n *Neb) StartPprof(string) error { return nil } + +func (n *Neb) SetGenesis(genesis *corepb.Genesis) { + n.genesis = genesis +} + +var ( + DefaultOpenDynasty = []string{ + "n1FF1nz6tarkDVwWQkMnnwFPuPKUaQTdptE", + "n1GmkKH6nBMw4rrjt16RrJ9WcgvKUtAZP1s", + "n1H4MYms9F55ehcvygwWE71J8tJC4CRr2so", + "n1JAy4X6KKLCNiTd7MWMRsVBjgdVq5WCCpf", + "n1LkDi2gGMqPrjYcczUiweyP4RxTB6Go1qS", + "n1LmP9K8pFF33fgdgHZonFEMsqZinJ4EUqk", + "n1MNXBKm6uJ5d76nJTdRvkPNVq85n6CnXAi", + "n1NrMKTYESZRCwPFDLFKiKREzZKaN1nhQvz", + "n1NwoSCDFwFL2981k6j9DPooigW33hjAgTa", + "n1PfACnkcfJoNm1Pbuz55pQCwueW1BYs83m", + "n1Q8mxXp4PtHaXtebhY12BnHEwu4mryEkXH", + "n1RYagU8n3JSuV4R7q4Qs5gQJ3pEmrZd6cJ", + "n1SAQy3ix1pZj8MPzNeVqpAmu1nCVqb5w8c", + "n1SHufJdxt2vRWGKAxwPETYfEq3MCQXnEXE", + "n1SSda41zGr9FKF5DJNE2ryY1ToNrndMauN", + "n1TmQtaCn3PNpk4f4ycwrBxCZFSVKvwBtzc", + "n1UM7z6MqnGyKEPvUpwrfxZpM1eB7UpzmLJ", + "n1UnCsJZjQiKyQiPBr7qG27exqCLuWUf1d7", + "n1XkoVVjswb5Gek3rRufqjKNpwrDdsnQ7Hq", + "n1cYKNHTeVW9v1NQRWuhZZn9ETbqAYozckh", + "n1dYu2BXgV3xgUh8LhZu8QDDNr15tz4hVDv", + } +) + +// MockGenesisConf return mock genesis conf +func MockGenesisConf() *corepb.Genesis { + dynasty := []string{} + for _, v := range DefaultOpenDynasty { + dynasty = append(dynasty, v) + } + return &corepb.Genesis{ + Meta: &corepb.GenesisMeta{ChainId: 0}, + Consensus: &corepb.GenesisConsensus{ + Dpos: &corepb.GenesisConsensusDpos{ + Dynasty: dynasty, + }, + }, + TokenDistribution: []*corepb.GenesisTokenDistribution{ + &corepb.GenesisTokenDistribution{ + Address: "n1FF1nz6tarkDVwWQkMnnwFPuPKUaQTdptE", + Value: "5000000000000000000000000", + }, + &corepb.GenesisTokenDistribution{ + Address: "n1GmkKH6nBMw4rrjt16RrJ9WcgvKUtAZP1s", + Value: "5000000000000000000000000", + }, + &corepb.GenesisTokenDistribution{ + Address: "n1H4MYms9F55ehcvygwWE71J8tJC4CRr2so", + Value: "5000000000000000000000000", + }, + &corepb.GenesisTokenDistribution{ + Address: "n1JAy4X6KKLCNiTd7MWMRsVBjgdVq5WCCpf", + Value: "5000000000000000000000000", + }, + &corepb.GenesisTokenDistribution{ + Address: "n1LkDi2gGMqPrjYcczUiweyP4RxTB6Go1qS", + Value: "5000000000000000000000000", + }, + &corepb.GenesisTokenDistribution{ + Address: "n1LmP9K8pFF33fgdgHZonFEMsqZinJ4EUqk", + Value: "5000000000000000000000000", + }, + }, + } +} + +var ( + received = []byte{} +) + +type mockNetService struct{} + +func (n mockNetService) Start() error { return nil } +func (n mockNetService) Stop() {} + +func (n mockNetService) Node() *net.Node { return nil } + +func (n mockNetService) Sync(net.Serializable) error { return nil } + +func (n mockNetService) Register(...*net.Subscriber) {} +func (n mockNetService) Deregister(...*net.Subscriber) {} + +func (n mockNetService) Broadcast(name string, msg net.Serializable, priority int) { + pb, _ := msg.ToProto() + bytes, _ := proto.Marshal(pb) + received = bytes +} +func (n mockNetService) Relay(name string, msg net.Serializable, priority int) { + pb, _ := msg.ToProto() + bytes, _ := proto.Marshal(pb) + received = bytes +} +func (n mockNetService) SendMsg(name string, msg []byte, target string, priority int) error { + received = msg + return nil +} + +func (n mockNetService) SendMessageToPeers(messageName string, data []byte, priority int, filter net.PeerFilterAlgorithm) []string { + return make([]string, 0) +} +func (n mockNetService) SendMessageToPeer(messageName string, data []byte, priority int, peerID string) error { + return nil +} + +func (n mockNetService) ClosePeer(peerID string, reason error) {} + +func (n mockNetService) BroadcastNetworkID([]byte) {} + +type contract struct { + contractPath string + sourceType string + initArgs string +} + +type call struct { + function string + args string +} + +func TestInnerTransactions(t *testing.T) { + tests := []struct { + name string + contracts []contract + call call + }{ + { + "deploy test_require_module.js", + []contract{ + contract{ + "./test/bank_vault_contract.js", + "js", + "", + }, + contract{ + "./test/bank_vault_contract.js", + "js", + "", + }, + }, + call{ + "save", + "[1]", + }, + }, + } + + for _, tt := range tests { + neb := mockNeb(t) + tail := neb.chain.TailBlock() + manager, err := account.NewManager(neb) + assert.Nil(t, err) + + a, _ := core.AddressParse("n1FF1nz6tarkDVwWQkMnnwFPuPKUaQTdptE") + assert.Nil(t, manager.Unlock(a, []byte("passphrase"), keystore.YearUnlockDuration)) + b, _ := core.AddressParse("n1GmkKH6nBMw4rrjt16RrJ9WcgvKUtAZP1s") + assert.Nil(t, manager.Unlock(b, []byte("passphrase"), keystore.YearUnlockDuration)) + c, _ := core.AddressParse("n1H4MYms9F55ehcvygwWE71J8tJC4CRr2so") + assert.Nil(t, manager.Unlock(c, []byte("passphrase"), keystore.YearUnlockDuration)) + + elapsedSecond := dpos.BlockIntervalInMs / dpos.SecondInMs + consensusState, err := tail.WorldState().NextConsensusState(elapsedSecond) + assert.Nil(t, err) + block, err := core.NewBlock(neb.chain.ChainID(), b, tail) + assert.Nil(t, err) + block.WorldState().SetConsensusState(consensusState) + block.SetTimestamp(consensusState.TimeStamp()) + + contractsAddr := []string{} + + t.Run(tt.name, func(t *testing.T) { + for k, v := range tt.contracts { + data, err := ioutil.ReadFile(v.contractPath) + assert.Nil(t, err, "contract path read error") + source := string(data) + sourceType := "js" + argsDeploy := "" + deploy, _ := core.NewDeployPayload(source, sourceType, argsDeploy) + payloadDeploy, _ := deploy.ToBytes() + + value, _ := util.NewUint128FromInt(0) + gasLimit, _ := util.NewUint128FromInt(200000) + txDeploy, err := core.NewTransaction(neb.chain.ChainID(), a, a, value, uint64(k+1), core.TxPayloadDeployType, payloadDeploy, core.TransactionGasPrice, gasLimit) + assert.Nil(t, err) + assert.Nil(t, manager.SignTransaction(a, txDeploy)) + assert.Nil(t, neb.chain.TransactionPool().Push(txDeploy)) + + contractAddr, err := txDeploy.GenerateContractAddress() + assert.Nil(t, err) + contractsAddr = append(contractsAddr, contractAddr.String()) + } + }) + + block.CollectTransactions((time.Now().Unix() + 1) * dpos.SecondInMs) + assert.Nil(t, block.Seal()) + assert.Nil(t, manager.SignBlock(b, block)) + assert.Nil(t, neb.chain.BlockPool().Push(block)) + + for _, v := range contractsAddr { + contract, err := core.AddressParse(v) + assert.Nil(t, err) + _, err = neb.chain.TailBlock().CheckContract(contract) + assert.Nil(t, err) + } + + elapsedSecond = dpos.BlockIntervalInMs / dpos.SecondInMs + tail = neb.chain.TailBlock() + consensusState, err = tail.WorldState().NextConsensusState(elapsedSecond) + assert.Nil(t, err) + block, err = core.NewBlock(neb.chain.ChainID(), c, tail) + assert.Nil(t, err) + block.WorldState().SetConsensusState(consensusState) + block.SetTimestamp(consensusState.TimeStamp()) + + callPayload, _ := core.NewCallPayload(tt.call.function, tt.call.args) + payloadCall, _ := callPayload.ToBytes() + + value, _ := util.NewUint128FromInt(0) + gasLimit, _ := util.NewUint128FromInt(200000) + txCall, err := core.NewTransaction(neb.chain.ChainID(), a, a, value, uint64(len(contractsAddr)+1), core.TxPayloadCallType, payloadCall, core.TransactionGasPrice, gasLimit) + assert.Nil(t, err) + assert.Nil(t, manager.SignTransaction(a, txCall)) + assert.Nil(t, neb.chain.TransactionPool().Push(txCall)) + + block.CollectTransactions((time.Now().Unix() + 1) * dpos.SecondInMs) + assert.Nil(t, block.Seal()) + assert.Nil(t, manager.SignBlock(c, block)) + assert.Nil(t, neb.chain.BlockPool().Push(block)) + + // check + } +} diff --git a/nf/nvm/event.go b/nf/nvm/event.go index 25fdb587f..3f3bac940 100644 --- a/nf/nvm/event.go +++ b/nf/nvm/event.go @@ -32,6 +32,13 @@ const ( EventBaseGasCount = 20 ) +// TransferFromContractEvent event for transfer in contract +type TransferFromContractEvent struct { + Amount string `json:"amount"` + From string `json:"from"` + To string `json:"to"` +} + // EventTriggerFunc export EventTriggerFunc //export EventTriggerFunc func EventTriggerFunc(handler unsafe.Pointer, topic, data *C.char, gasCnt *C.size_t) { diff --git a/nf/nvm/keydir b/nf/nvm/keydir new file mode 120000 index 000000000..188b3b5d4 --- /dev/null +++ b/nf/nvm/keydir @@ -0,0 +1 @@ +../../keydir/ \ No newline at end of file diff --git a/nf/nvm/lib/date.js b/nf/nvm/lib/date.js new file mode 120000 index 000000000..b2d62a9aa --- /dev/null +++ b/nf/nvm/lib/date.js @@ -0,0 +1 @@ +../v8/lib/date.js \ No newline at end of file diff --git a/nf/nvm/lib/random.js b/nf/nvm/lib/random.js new file mode 120000 index 000000000..6d184e127 --- /dev/null +++ b/nf/nvm/lib/random.js @@ -0,0 +1 @@ +../v8/lib/random.js \ No newline at end of file diff --git a/nf/nvm/test/contract_accept_func_standard.js b/nf/nvm/test/contract_accept_func_standard.js new file mode 100644 index 000000000..c5ca72a81 --- /dev/null +++ b/nf/nvm/test/contract_accept_func_standard.js @@ -0,0 +1,107 @@ + +"use strict"; + +var DepositeContent = function (text) { + if (text) { + var o = JSON.parse(text); + this.balance = new BigNumber(o.balance); + this.expiryHeight = new BigNumber(o.expiryHeight); + } else { + this.balance = new BigNumber(0); + this.expiryHeight = new BigNumber(0); + } +}; + +DepositeContent.prototype = { + toString: function () { + return JSON.stringify(this); + } +}; + +var BankVaultContract = function () { + LocalContractStorage.defineMapProperty(this, "bankVault", { + parse: function (text) { + return new DepositeContent(text); + }, + stringify: function (o) { + return o.toString(); + } + }); +}; + +// save value to contract, only after height of block, users can takeout +BankVaultContract.prototype = { + init: function () { + //TODO: + }, + + // enables accepting NAS via 'binary' + accept: function() {}, + + save: function (height) { + var from = Blockchain.transaction.from; + var value = Blockchain.transaction.value; + var bk_height = new BigNumber(Blockchain.block.height); + + var orig_deposit = this.bankVault.get(from); + if (orig_deposit) { + value = value.plus(orig_deposit.balance); + } + + var deposit = new DepositeContent(); + deposit.balance = value; + deposit.expiryHeight = bk_height.plus(height); + + this.bankVault.put(from, deposit); + }, + + takeout: function (value) { + var from = Blockchain.transaction.from; + var bk_height = new BigNumber(Blockchain.block.height); + var amount = new BigNumber(value); + + var deposit = this.bankVault.get(from); + if (!deposit) { + throw new Error("No deposit before."); + } + + if (bk_height.lt(deposit.expiryHeight)) { + throw new Error("Can not takeout before expiryHeight."); + } + + if (amount.gt(deposit.balance)) { + throw new Error("Insufficient balance."); + } + + var result = Blockchain.transfer(from, amount); + if (!result) { + throw new Error("transfer failed."); + } + Event.Trigger("BankVault", { + Transfer: { + from: Blockchain.transaction.to, + to: from, + value: amount.toString() + } + }); + + deposit.balance = deposit.balance.sub(amount); + this.bankVault.put(from, deposit); + }, + + balanceOf: function () { + var from = Blockchain.transaction.from; + return this.bankVault.get(from); + }, + + verifyAddress: function (address) { + // 1-valid, 0-invalid + var result = Blockchain.verifyAddress(address); + return { + valid: result == 0 ? false : true + }; + } +}; + +module.exports = BankVaultContract; + diff --git a/nf/nvm/test/contract_accept_func_with_args.js b/nf/nvm/test/contract_accept_func_with_args.js new file mode 100644 index 000000000..87dcb602a --- /dev/null +++ b/nf/nvm/test/contract_accept_func_with_args.js @@ -0,0 +1,58 @@ + +"use strict"; + +var DepositeContent = function (text) { + if (text) { + var o = JSON.parse(text); + this.balance = new BigNumber(o.balance); + this.expiryHeight = new BigNumber(o.expiryHeight); + } else { + this.balance = new BigNumber(0); + this.expiryHeight = new BigNumber(0); + } +}; + +DepositeContent.prototype = { + toString: function () { + return JSON.stringify(this); + } +}; + +var BankVaultContract = function () { + LocalContractStorage.defineMapProperty(this, "bankVault", { + parse: function (text) { + return new DepositeContent(text); + }, + stringify: function (o) { + return o.toString(); + } + }); +}; + +// save value to contract, only after height of block, users can takeout +BankVaultContract.prototype = { + init: function () { + //TODO: + }, + + // enables accepting NAS via 'binary' + accept: function (height) { + var from = Blockchain.transaction.from; + var value = Blockchain.transaction.value; + var bk_height = new BigNumber(Blockchain.block.height); + + var orig_deposit = this.bankVault.get(from); + if (orig_deposit) { + value = value.plus(orig_deposit.balance); + } + + var deposit = new DepositeContent(); + deposit.balance = value; + deposit.expiryHeight = bk_height.plus(height); + + this.bankVault.put(from, deposit); + } +}; + +module.exports = BankVaultContract; + diff --git a/nf/nvm/test/contract_accept_func_with_args_2.js b/nf/nvm/test/contract_accept_func_with_args_2.js new file mode 100644 index 000000000..83c2026c5 --- /dev/null +++ b/nf/nvm/test/contract_accept_func_with_args_2.js @@ -0,0 +1,56 @@ + +"use strict"; + +var DepositeContent = function (text) { + if (text) { + var o = JSON.parse(text); + this.balance = new BigNumber(o.balance); + this.expiryHeight = new BigNumber(o.expiryHeight); + } else { + this.balance = new BigNumber(0); + this.expiryHeight = new BigNumber(0); + } +}; + +DepositeContent.prototype = { + toString: function () { + return JSON.stringify(this); + } +}; + +var BankVaultContract = function () { + LocalContractStorage.defineMapProperty(this, "bankVault", { + parse: function (text) { + return new DepositeContent(text); + }, + stringify: function (o) { + return o.toString(); + } + }); +}; + +// save value to contract, only after height of block, users can takeout +BankVaultContract.prototype = { + init: function () { + //TODO: + }, + + // enables accepting NAS via 'binary' + accept: function (height) { + var from = Blockchain.transaction.from; + var value = Blockchain.transaction.value; + var bk_height = new BigNumber(Blockchain.block.height); + + console.log("accept height: " + height); + + return { + height: height, + from: from, + value: value, + bk_height: bk_height + }; + } +}; + +module.exports = BankVaultContract; + diff --git a/nf/nvm/test/contract_date_and_random.js b/nf/nvm/test/contract_date_and_random.js new file mode 100644 index 000000000..1e2a7c45b --- /dev/null +++ b/nf/nvm/test/contract_date_and_random.js @@ -0,0 +1,79 @@ +"use strict"; + +var Contract = function() { + +}; + +Contract.prototype = { + init: function(){}, + accept: 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) + }; + Event.Trigger("Date", { + data: data + }); + + date.setFullYear(2000); + Event.Trigger("Date.modi", { + data: { + valueOf: date.valueOf(), + date_toString: date.toString(), + date_getTime: date.getTime(), + date_getFullYear: date.getFullYear(), + equalBlockTime: Blockchain.block.timestamp == (date.getTime() / 1000) + } + }); + + return data; + }, + + testDate2: function() { + var date = new Date(); + + Event.Trigger("Date.TZ", { + timezone: date.getTimezoneOffset() + }); + }, + + testRandom: function(userseed) { + var r1 = Math.random(); + var r12 = Math.random(); + var r13 = Math.random(); + // equivalent to reset seed + Math.random.seed(userseed); + var r2 = Math.random(); + + Event.Trigger("random", { + "seed": Blockchain.block.seed, + "defaultSeedRandom1": r1, + "defaultSeedRandom2": r12, + "defaultSeedRandom3": r13, + "userSeedRandom": r2 + }); + } +}; + +module.exports = Contract; \ No newline at end of file diff --git a/nf/nvm/test/test_date.js b/nf/nvm/test/test_date.js index 9572371a4..4ed43720c 100644 --- a/nf/nvm/test/test_date.js +++ b/nf/nvm/test/test_date.js @@ -16,9 +16,126 @@ // along with the go-nebulas library. If not, see . // +Blockchain.blockParse("{\"timestamp\":20000000000,\"seed\":\"\"}"); console.log(Date.now()); console.log(Date.UTC()); +console.log(Date.parse('01 Jan 1970 00:00:00 GMT')); -var date = new Date(); -console.log(date.getTime()); \ No newline at end of file +var date = new Date('August 19, 2017 23:15:30'); +var date2 = new Date('August 19, 2017 23:15:30'); +console.log(date.getTime()); +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()); +eq(date.toString(), date.toUTCString()); + +try { + date.getTimezoneOffset(); + throw new Error("should not be here."); +} catch(err) { + if (err != "Error: Unsupported method!") { + throw err; + } +} +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; + } +} +try { + date.toDateString(); + throw new Error("should not be here."); +} catch(err) { + if (err != "Error: Unsupported method!") { + throw err; + } +} +try { + date.toTimeString(); + throw new Error("should not be here."); +} catch(err) { + if (err != "Error: Unsupported method!") { + throw err; + } +} + +var tmp = new Date('August 19, 1975 23:15:30 UTC'); +if (tmp.toJSON() !== "1975-08-19T23:15:30.000Z") { + throw new Error("toJSON is not equal.") +} +try { + date.toLocaleDateString(); + throw new Error("should not be here."); +} catch(err) { + if (err != "Error: Unsupported method!") { + throw err; + } +} +try { + date.toLocaleTimeString(); + throw new Error("should not be here."); +} catch(err) { + if (err != "Error: Unsupported method!") { + throw err; + } +} + +try { + date.toLocaleString(); + throw new Error("should not be here."); +} catch(err) { + if (err != "Error: Unsupported method!") { + throw err; + } +} + +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); + +function eq(a, b) { + if (a !== b) { + throw new Error("Not equal."); + } +} \ No newline at end of file diff --git a/nf/nvm/test/test_random_disable.js b/nf/nvm/test/test_random_disable.js new file mode 100644 index 000000000..c80bf29a0 --- /dev/null +++ b/nf/nvm/test/test_random_disable.js @@ -0,0 +1,21 @@ +// 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 . +// + +Blockchain.blockParse("{}"); + +console.log(Math.random()); \ No newline at end of file diff --git a/nf/nvm/test/test_random.js b/nf/nvm/test/test_random_enable.js similarity index 94% rename from nf/nvm/test/test_random.js rename to nf/nvm/test/test_random_enable.js index 75895f922..890f9eedb 100644 --- a/nf/nvm/test/test_random.js +++ b/nf/nvm/test/test_random_enable.js @@ -16,4 +16,6 @@ // along with the go-nebulas library. If not, see . // +Blockchain.blockParse("{\"seed\":\"seed\"}"); + console.log(Math.random()); diff --git a/nf/nvm/test/test_random_seed.js b/nf/nvm/test/test_random_seed.js new file mode 100644 index 000000000..b07416b3e --- /dev/null +++ b/nf/nvm/test/test_random_seed.js @@ -0,0 +1,20 @@ +// 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 . +// + +Blockchain.blockParse("{\"seed\":\"null\"}"); +console.log(Math.random.seed()); \ No newline at end of file diff --git a/nf/nvm/types.go b/nf/nvm/types.go index ead9b4634..5571fe802 100644 --- a/nf/nvm/types.go +++ b/nf/nvm/types.go @@ -48,6 +48,7 @@ const ( TransferStringToBigIntErr TransferSubBalance TransferAddBalance + TransferRecordEventFailed ) // Block interface breaks cycle import dependency and hides unused services. @@ -55,6 +56,9 @@ type Block interface { Hash() byteutils.Hash Height() uint64 // ToAdd: timestamp interface Timestamp() int64 + RandomSeed() string + RandomAvailable() bool + DateAvailable() bool } // Transaction interface breaks cycle import dependency and hides unused services. @@ -71,6 +75,7 @@ type Transaction interface { // Account interface breaks cycle import dependency and hides unused services. type Account interface { + Address() byteutils.Hash Balance() *util.Uint128 Nonce() uint64 AddBalance(value *util.Uint128) error diff --git a/nf/nvm/v8/lib/blockchain.js b/nf/nvm/v8/lib/blockchain.js index d515dee86..529d1be22 100644 --- a/nf/nvm/v8/lib/blockchain.js +++ b/nf/nvm/v8/lib/blockchain.js @@ -19,7 +19,13 @@ 'use strict'; var Blockchain = function () { - this.nativeBlockchain = _native_blockchain; + Object.defineProperty(this, "nativeBlockchain", { + configurable: false, + enumerable: false, + get: function(){ + return _native_blockchain; + } + }); }; Blockchain.prototype = { @@ -29,7 +35,14 @@ Blockchain.prototype = { blockParse: function (str) { var block = JSON.parse(str); if (block != null) { - this.block = Object.freeze(block); + var fb = Object.freeze(block); + Object.defineProperty(this, "block", { + configurable: false, + enumerable: false, + get: function(){ + return fb; + } + }); } }, transactionParse: function (str) { @@ -41,7 +54,15 @@ Blockchain.prototype = { tx.gasPrice = new BigNumber(gasPrice); var gasLimit = tx.gasLimit === undefined || tx.gasLimit.length === 0 ? "0" : tx.gasLimit; tx.gasLimit = new BigNumber(gasLimit); - this.transaction = Object.freeze(tx); + + var ft = Object.freeze(tx); + Object.defineProperty(this, "transaction", { + configurable: false, + enumerable: false, + get: function(){ + return ft; + } + }); } }, transfer: function (address, value) { @@ -55,5 +76,4 @@ Blockchain.prototype = { return this.nativeBlockchain.verifyAddress(address); } }; - module.exports = new Blockchain(); diff --git a/nf/nvm/v8/lib/date.js b/nf/nvm/v8/lib/date.js new file mode 100644 index 000000000..d7ed998cb --- /dev/null +++ b/nf/nvm/v8/lib/date.js @@ -0,0 +1,143 @@ +// 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) { + // compatibility + var Date = function() { + throw new Error("Date is not allowed in nvm."); + } + + function allow() { + return Blockchain.block.seed != null && typeof(Blockchain.block.seed) !== 'undefined'; + } + + function NebDate() { + if (!Blockchain) { + throw new Error("'Blockchain' is not defined."); + } + if (!Blockchain.block) { + throw new Error("'Blockchain.block' is not defined."); + } + if (!allow()) { + throw new Error("Date is not allowed in nvm."); + } + + 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() { + if (!allow()) { + Date.now(); + } + return new NebDate().getTime(); + } + NebDate.UTC = function() { + if (!allow()) { + Date.UTC(); + } + return ProtoDate.UTC.apply(null, arguments); + } + NebDate.parse = function(dateString) { + if (!allow()) { + Date.parse(dateString); + } + return ProtoDate.parse(dateString); + } + + NebDate.prototype.getTimezoneOffset = function() { + throw new Error("Unsupported method!"); + } + NebDate.prototype.getDate = function() { + return this.getUTCDate(); + } + NebDate.prototype.getDay = function() { + return this.getUTCDay(); + } + NebDate.prototype.getFullYear = function() { + return this.getUTCFullYear(); + } + NebDate.prototype.getHours = function() { + return this.getUTCHours(); + } + NebDate.prototype.getMilliseconds = function() { + return this.getUTCMilliseconds(); + } + NebDate.prototype.getMinutes = function() { + return this.getUTCMinutes(); + } + NebDate.prototype.getMonth = function() { + return this.getUTCMonth(); + } + NebDate.prototype.getSeconds = function() { + return this.getUTCSeconds(); + }, + NebDate.prototype.getYear = function() { + throw new Error("Deprecated!"); + } + NebDate.prototype.setYear = function() { + throw new Error("Deprecated!"); + } + NebDate.prototype.setDate = function() { + return this.setUTCDate.apply(this, arguments); + } + NebDate.prototype.setFullYear = function() { + return this.setUTCFullYear.apply(this, arguments); + } + NebDate.prototype.setHours = function() { + return this.setUTCHours.apply(this, arguments); + } + NebDate.prototype.setMilliseconds = function() { + return this.setUTCMilliseconds.apply(this, arguments); + } + NebDate.prototype.setMinutes = function() { + return this.setUTCMinutes.apply(this, arguments); + } + NebDate.prototype.setMonth = function() { + return this.setUTCMonth.apply(this, arguments); + } + NebDate.prototype.setSeconds = function() { + return this.setUTCSeconds.apply(this, arguments); + } + NebDate.prototype.toString = function() { + // return UTC string + return this.toUTCString.apply(this, arguments); + } + NebDate.prototype.toDateString = function() { + throw new Error("Unsupported method!"); + } + NebDate.prototype.toTimeString = function() { + throw new Error("Unsupported method!"); + } + + 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/event.js b/nf/nvm/v8/lib/event.js index 791830999..13bdb68c1 100644 --- a/nf/nvm/v8/lib/event.js +++ b/nf/nvm/v8/lib/event.js @@ -20,4 +20,4 @@ exports["Trigger"] = function (topic, data) { _native_event_trigger(topic, JSON.stringify(data)); -}; +}; \ No newline at end of file diff --git a/nf/nvm/v8/lib/execution_env.js b/nf/nvm/v8/lib/execution_env.js index d9fbf5ce5..27a23767c 100644 --- a/nf/nvm/v8/lib/execution_env.js +++ b/nf/nvm/v8/lib/execution_env.js @@ -16,13 +16,7 @@ // along with the go-nebulas library. If not, see . // -const Date = function () { - throw new Error("Date is not allowed in nvm."); -}; - -Math.random = function () { - throw new Error("Math.random func is not allowed in nvm."); -}; +Function.prototype.toString = function(){return "";}; const require = (function (global) { var PathRegexForNotLibFile = /^\.{0,2}\//; @@ -116,3 +110,6 @@ const GlobalContractStorage = ContractStorage.gcs; const BigNumber = require('bignumber.js'); const Blockchain = require('blockchain.js'); const Event = require('event.js'); + +var Date = require('date.js'); +Math.random = require('random.js'); \ No newline at end of file diff --git a/nf/nvm/v8/lib/random.js b/nf/nvm/v8/lib/random.js new file mode 100644 index 000000000..26bb975e1 --- /dev/null +++ b/nf/nvm/v8/lib/random.js @@ -0,0 +1,131 @@ +// 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") + } + checkCtx(); + arng = new impl(Blockchain.block.seed + userseed); + } + + return rand; +})(); \ No newline at end of file diff --git a/nf/nvm/v8/lib/storage.js b/nf/nvm/v8/lib/storage.js index c0f57ce7f..f4f7deaa4 100644 --- a/nf/nvm/v8/lib/storage.js +++ b/nf/nvm/v8/lib/storage.js @@ -79,7 +79,14 @@ var applyFieldDescriptor = function (obj, fieldName, descriptor) { }; var ContractStorage = function (handler) { - this.nativeStorage = new NativeStorage(handler); + var ns = new NativeStorage(handler); + Object.defineProperty(this, "nativeStorage", { + configurable: false, + enumerable: false, + get: function () { + return ns; + } + }); }; var StorageMap = function (contractStorage, fieldName, descriptor) { @@ -221,8 +228,23 @@ ContractStorage.prototype = { ContractStorage.prototype.put = ContractStorage.prototype.set; ContractStorage.prototype.delete = ContractStorage.prototype.del; -module.exports = Object.freeze({ - ContractStorage: ContractStorage, - lcs: new ContractStorage(_native_storage_handlers.lcs), - gcs: new ContractStorage(_native_storage_handlers.gcs) +var lcs = new ContractStorage(_native_storage_handlers.lcs); +var gcs = new ContractStorage(_native_storage_handlers.gcs); +var obj = {ContractStorage: ContractStorage}; +Object.defineProperty(obj, "lcs", { + configurable: false, + enumerable: false, + get: function () { + return lcs; + } }); + +Object.defineProperty(obj, "gcs", { + configurable: false, + enumerable: false, + get: function () { + return gcs; + } +}); + +module.exports = Object.freeze(obj); \ No newline at end of file diff --git a/rpc/admin_service.go b/rpc/admin_service.go index e97f13154..20d7ef6aa 100644 --- a/rpc/admin_service.go +++ b/rpc/admin_service.go @@ -136,6 +136,30 @@ func (s *AdminService) SignHash(ctx context.Context, req *rpcpb.SignHashRequest) return &rpcpb.SignHashResponse{Data: data}, nil } +// GenerateRandomSeed generate block's rand info +func (s *AdminService) GenerateRandomSeed(ctx context.Context, req *rpcpb.GenerateRandomSeedRequest) (*rpcpb.GenerateRandomSeedResponse, error) { + neb := s.server.Neblet() + inputs, err := neb.BlockChain().GetInputForVRFSigner(req.ParentHash, req.Height) + if err != nil { + return nil, err + } + + addr, err := core.AddressParse(req.Address) + if err != nil { + return nil, err + } + + vrfSeed, vrfProof, err := neb.AccountManager().GenerateRandomSeed(addr, inputs...) + if err != nil { + return nil, err + } + + return &rpcpb.GenerateRandomSeedResponse{ + VrfSeed: vrfSeed, + VrfProof: vrfProof, + }, nil +} + // SignTransactionWithPassphrase sign transaction with the from addr passphrase func (s *AdminService) SignTransactionWithPassphrase(ctx context.Context, req *rpcpb.SignTransactionPassphraseRequest) (*rpcpb.SignTransactionPassphraseResponse, error) { diff --git a/rpc/api_service.go b/rpc/api_service.go index 23631a782..e29e1464f 100644 --- a/rpc/api_service.go +++ b/rpc/api_service.go @@ -141,46 +141,94 @@ func parseTransaction(neb core.Neblet, reqTx *rpcpb.TransactionRequest) (*core.T if err != nil { return nil, errors.New("invalid gasLimit") } - var ( - payloadType string - payload []byte - ) - if reqTx.Contract != nil { - if len(reqTx.Contract.Source) > 0 && len(reqTx.Contract.Function) == 0 { // TODO: reqTx.DeployContract, reqTx.CallContract - payloadType = core.TxPayloadDeployType - payloadObj, err := core.NewDeployPayload(reqTx.Contract.Source, reqTx.Contract.SourceType, reqTx.Contract.Args) - if err != nil { - return nil, err + payloadType, payload, err := parseTransactionPayload(reqTx) + if err != nil { + return nil, err + } + + tx, err := core.NewTransaction(neb.BlockChain().ChainID(), fromAddr, toAddr, value, reqTx.Nonce, payloadType, payload, gasPrice, gasLimit) + if err != nil { + return nil, err + } + return tx, nil +} + +func parseTransactionPayload(reqTx *rpcpb.TransactionRequest) (payloadType string, payload []byte, err error) { + if len(reqTx.Type) > 0 { + switch reqTx.Type { + case core.TxPayloadBinaryType: + { + if payload, err = core.NewBinaryPayload(reqTx.Binary).ToBytes(); err != nil { + return "", nil, err + } } - if payload, err = payloadObj.ToBytes(); err != nil { - return nil, err + case core.TxPayloadDeployType: + { + if reqTx.Contract == nil { + return "", nil, core.ErrInvalidDeploySource + } + deployPayload, err := core.NewDeployPayload(reqTx.Contract.Source, reqTx.Contract.SourceType, reqTx.Contract.Args) + if err != nil { + return "", nil, err + } + if payload, err = deployPayload.ToBytes(); err != nil { + return "", nil, err + } } - } else if len(reqTx.Contract.Source) == 0 && len(reqTx.Contract.Function) > 0 { - payloadType = core.TxPayloadCallType - callpayload, err := core.NewCallPayload(reqTx.Contract.Function, reqTx.Contract.Args) + case core.TxPayloadCallType: + { + if reqTx.Contract == nil { + return "", nil, core.ErrInvalidCallFunction + } + callpayload, err := core.NewCallPayload(reqTx.Contract.Function, reqTx.Contract.Args) + if err != nil { + return "", nil, err + } + + if payload, err = callpayload.ToBytes(); err != nil { + return "", nil, err + } + } + default: + return "", nil, core.ErrInvalidTxPayloadType + } + } else { + if reqTx.Contract != nil { + toAddr, err := core.AddressParse(reqTx.To) if err != nil { - return nil, err + return "", nil, err } - - if payload, err = callpayload.ToBytes(); err != nil { - return nil, err + if len(reqTx.Contract.Source) > 0 && len(reqTx.Contract.Function) == 0 && reqTx.From == reqTx.To { + payloadType = core.TxPayloadDeployType + payloadObj, err := core.NewDeployPayload(reqTx.Contract.Source, reqTx.Contract.SourceType, reqTx.Contract.Args) + if err != nil { + return "", nil, err + } + if payload, err = payloadObj.ToBytes(); err != nil { + return "", nil, err + } + } else if len(reqTx.Contract.Source) == 0 && len(reqTx.Contract.Function) > 0 && toAddr.Type() == core.ContractAddress { + payloadType = core.TxPayloadCallType + callpayload, err := core.NewCallPayload(reqTx.Contract.Function, reqTx.Contract.Args) + if err != nil { + return "", nil, err + } + + if payload, err = callpayload.ToBytes(); err != nil { + return "", nil, err + } + } else { + return "", nil, errors.New("invalid contract") } } else { - return nil, errors.New("invalid contract") - } - } else { - payloadType = core.TxPayloadBinaryType - if payload, err = core.NewBinaryPayload(reqTx.Binary).ToBytes(); err != nil { - return nil, err + payloadType = core.TxPayloadBinaryType + if payload, err = core.NewBinaryPayload(reqTx.Binary).ToBytes(); err != nil { + return "", nil, err + } } } - - tx, err := core.NewTransaction(neb.BlockChain().ChainID(), fromAddr, toAddr, value, reqTx.Nonce, payloadType, payload, gasPrice, gasLimit) - if err != nil { - return nil, err - } - return tx, nil + return payloadType, payload, nil } func handleTransactionResponse(neb core.Neblet, tx *core.Transaction) (resp *rpcpb.SendTransactionResponse, err error) { @@ -192,11 +240,6 @@ func handleTransactionResponse(neb core.Neblet, tx *core.Transaction) (resp *rpc } }() - err = tx.VerifyIntegrity(neb.BlockChain().ChainID()) - if err != nil { - return nil, err - } - tailBlock := neb.BlockChain().TailBlock() acc, err := tailBlock.GetAccount(tx.From().Bytes()) if err != nil { @@ -207,6 +250,18 @@ func handleTransactionResponse(neb core.Neblet, tx *core.Transaction) (resp *rpc return nil, errors.New("transaction's nonce is invalid, should bigger than the from's nonce") } + // check Balance Simulate + if tx.Nonce() == (acc.Nonce() + 1) { + result, err := neb.BlockChain().SimulateTransactionExecution(tx) + if err != nil { + return nil, err + } + + if result.Err != nil { + return nil, result.Err + } + } + if tx.Type() == core.TxPayloadDeployType { if !tx.From().Equals(tx.To()) { return nil, core.ErrContractTransactionAddressNotEqual @@ -355,8 +410,10 @@ func (s *APIService) GetTransactionReceipt(ctx context.Context, req *rpcpb.GetTr func (s *APIService) toTransactionResponse(tx *core.Transaction) (*rpcpb.TransactionResponse, error) { var ( - status int32 - gasUsed string + status int32 + gasUsed string + execute_error string + execute_result string ) neb := s.server.Neblet() event, err := neb.BlockChain().TailBlock().FetchExecutionResultEvent(tx.Hash()) @@ -365,31 +422,48 @@ func (s *APIService) toTransactionResponse(tx *core.Transaction) (*rpcpb.Transac } if event != nil { - txEvent := core.TransactionEvent{} - err := json.Unmarshal([]byte(event.Data), &txEvent) - if err != nil { - return nil, err + h := neb.BlockChain().TailBlock().Height() + if h >= core.RecordCallContractResultHeight { + txEvent2 := core.TransactionEventV2{} + + err := json.Unmarshal([]byte(event.Data), &txEvent2) + if err != nil { + return nil, err + } + status = int32(txEvent2.Status) + gasUsed = txEvent2.GasUsed + execute_error = txEvent2.Error + execute_result = txEvent2.ExecuteResult + } else { + txEvent := core.TransactionEvent{} + err := json.Unmarshal([]byte(event.Data), &txEvent) + if err != nil { + return nil, err + } + status = int32(txEvent.Status) + gasUsed = txEvent.GasUsed + execute_error = txEvent.Error } - status = int32(txEvent.Status) - gasUsed = txEvent.GasUsed } else { status = core.TxExecutionPendding } resp := &rpcpb.TransactionResponse{ - ChainId: tx.ChainID(), - Hash: tx.Hash().String(), - From: tx.From().String(), - To: tx.To().String(), - Value: tx.Value().String(), - Nonce: tx.Nonce(), - Timestamp: tx.Timestamp(), - Type: tx.Type(), - Data: tx.Data(), - GasPrice: tx.GasPrice().String(), - GasLimit: tx.GasLimit().String(), - Status: status, - GasUsed: gasUsed, + ChainId: tx.ChainID(), + Hash: tx.Hash().String(), + From: tx.From().String(), + To: tx.To().String(), + Value: tx.Value().String(), + Nonce: tx.Nonce(), + Timestamp: tx.Timestamp(), + Type: tx.Type(), + Data: tx.Data(), + GasPrice: tx.GasPrice().String(), + GasLimit: tx.GasLimit().String(), + Status: status, + GasUsed: gasUsed, + ExecuteError: execute_error, + ExecuteResult: execute_result, } if tx.Type() == core.TxPayloadDeployType { diff --git a/rpc/pb/rpc.pb.go b/rpc/pb/rpc.pb.go index ed29b0d89..7f1464351 100644 --- a/rpc/pb/rpc.pb.go +++ b/rpc/pb/rpc.pb.go @@ -37,6 +37,8 @@ It has these top-level messages: LockAccountResponse SignHashRequest SignHashResponse + GenerateRandomSeedRequest + GenerateRandomSeedResponse SignTransactionPassphraseRequest SignTransactionPassphraseResponse SendTransactionPassphraseRequest @@ -58,10 +60,8 @@ import _ "google.golang.org/genproto/googleapis/api/annotations" import consensuspb "github.com/nebulasio/go-nebulas/consensus/pb" import nebletpb "github.com/nebulasio/go-nebulas/neblet/pb" -import ( - context "golang.org/x/net/context" - grpc "google.golang.org/grpc" -) +import context "golang.org/x/net/context" +import grpc "google.golang.org/grpc" // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal @@ -470,6 +470,8 @@ type TransactionRequest struct { Contract *ContractRequest `protobuf:"bytes,7,opt,name=contract" json:"contract,omitempty"` // binary data for transaction Binary []byte `protobuf:"bytes,10,opt,name=binary,proto3" json:"binary,omitempty"` + // transaction payload type, enum:binary, deploy, call + Type string `protobuf:"bytes,20,opt,name=type,proto3" json:"type,omitempty"` } func (m *TransactionRequest) Reset() { *m = TransactionRequest{} } @@ -533,6 +535,13 @@ func (m *TransactionRequest) GetBinary() []byte { return nil } +func (m *TransactionRequest) GetType() string { + if m != nil { + return m.Type + } + return "" +} + type ContractRequest struct { // contract source code. Source string `protobuf:"bytes,1,opt,name=source,proto3" json:"source,omitempty"` @@ -851,6 +860,10 @@ type TransactionResponse struct { Status int32 `protobuf:"varint,13,opt,name=status,proto3" json:"status,omitempty"` // transaction gas used GasUsed string `protobuf:"bytes,14,opt,name=gas_used,json=gasUsed,proto3" json:"gas_used,omitempty"` + // contract execute error + ExecuteError string `protobuf:"bytes,15,opt,name=execute_error,json=executeError,proto3" json:"execute_error,omitempty"` + // contract execute result + ExecuteResult string `protobuf:"bytes,16,opt,name=execute_result,json=executeResult,proto3" json:"execute_result,omitempty"` } func (m *TransactionResponse) Reset() { *m = TransactionResponse{} } @@ -956,6 +969,20 @@ func (m *TransactionResponse) GetGasUsed() string { return "" } +func (m *TransactionResponse) GetExecuteError() string { + if m != nil { + return m.ExecuteError + } + return "" +} + +func (m *TransactionResponse) GetExecuteResult() string { + if m != nil { + return m.ExecuteResult + } + return "" +} + type NewAccountRequest struct { Passphrase string `protobuf:"bytes,1,opt,name=passphrase,proto3" json:"passphrase,omitempty"` } @@ -1119,6 +1146,65 @@ func (m *SignHashResponse) GetData() []byte { return nil } +type GenerateRandomSeedRequest struct { + // miner address + Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` + // parent hash of new block + ParentHash []byte `protobuf:"bytes,2,opt,name=parent_hash,json=parentHash,proto3" json:"parent_hash,omitempty"` + // height of new block + Height uint64 `protobuf:"varint,3,opt,name=height,proto3" json:"height,omitempty"` +} + +func (m *GenerateRandomSeedRequest) Reset() { *m = GenerateRandomSeedRequest{} } +func (m *GenerateRandomSeedRequest) String() string { return proto.CompactTextString(m) } +func (*GenerateRandomSeedRequest) ProtoMessage() {} +func (*GenerateRandomSeedRequest) Descriptor() ([]byte, []int) { return fileDescriptorRpc, []int{29} } + +func (m *GenerateRandomSeedRequest) GetAddress() string { + if m != nil { + return m.Address + } + return "" +} + +func (m *GenerateRandomSeedRequest) GetParentHash() []byte { + if m != nil { + return m.ParentHash + } + return nil +} + +func (m *GenerateRandomSeedRequest) GetHeight() uint64 { + if m != nil { + return m.Height + } + return 0 +} + +type GenerateRandomSeedResponse struct { + VrfSeed []byte `protobuf:"bytes,1,opt,name=vrf_seed,json=vrfSeed,proto3" json:"vrf_seed,omitempty"` + VrfProof []byte `protobuf:"bytes,2,opt,name=vrf_proof,json=vrfProof,proto3" json:"vrf_proof,omitempty"` +} + +func (m *GenerateRandomSeedResponse) Reset() { *m = GenerateRandomSeedResponse{} } +func (m *GenerateRandomSeedResponse) String() string { return proto.CompactTextString(m) } +func (*GenerateRandomSeedResponse) ProtoMessage() {} +func (*GenerateRandomSeedResponse) Descriptor() ([]byte, []int) { return fileDescriptorRpc, []int{30} } + +func (m *GenerateRandomSeedResponse) GetVrfSeed() []byte { + if m != nil { + return m.VrfSeed + } + return nil +} + +func (m *GenerateRandomSeedResponse) GetVrfProof() []byte { + if m != nil { + return m.VrfProof + } + return nil +} + type SignTransactionPassphraseRequest struct { // transaction struct Transaction *TransactionRequest `protobuf:"bytes,1,opt,name=transaction" json:"transaction,omitempty"` @@ -1130,7 +1216,7 @@ func (m *SignTransactionPassphraseRequest) Reset() { *m = SignTransactio func (m *SignTransactionPassphraseRequest) String() string { return proto.CompactTextString(m) } func (*SignTransactionPassphraseRequest) ProtoMessage() {} func (*SignTransactionPassphraseRequest) Descriptor() ([]byte, []int) { - return fileDescriptorRpc, []int{29} + return fileDescriptorRpc, []int{31} } func (m *SignTransactionPassphraseRequest) GetTransaction() *TransactionRequest { @@ -1155,7 +1241,7 @@ func (m *SignTransactionPassphraseResponse) Reset() { *m = SignTransacti func (m *SignTransactionPassphraseResponse) String() string { return proto.CompactTextString(m) } func (*SignTransactionPassphraseResponse) ProtoMessage() {} func (*SignTransactionPassphraseResponse) Descriptor() ([]byte, []int) { - return fileDescriptorRpc, []int{30} + return fileDescriptorRpc, []int{32} } func (m *SignTransactionPassphraseResponse) GetData() []byte { @@ -1176,7 +1262,7 @@ func (m *SendTransactionPassphraseRequest) Reset() { *m = SendTransactio func (m *SendTransactionPassphraseRequest) String() string { return proto.CompactTextString(m) } func (*SendTransactionPassphraseRequest) ProtoMessage() {} func (*SendTransactionPassphraseRequest) Descriptor() ([]byte, []int) { - return fileDescriptorRpc, []int{31} + return fileDescriptorRpc, []int{33} } func (m *SendTransactionPassphraseRequest) GetTransaction() *TransactionRequest { @@ -1200,7 +1286,7 @@ type GasPriceResponse struct { func (m *GasPriceResponse) Reset() { *m = GasPriceResponse{} } func (m *GasPriceResponse) String() string { return proto.CompactTextString(m) } func (*GasPriceResponse) ProtoMessage() {} -func (*GasPriceResponse) Descriptor() ([]byte, []int) { return fileDescriptorRpc, []int{32} } +func (*GasPriceResponse) Descriptor() ([]byte, []int) { return fileDescriptorRpc, []int{34} } func (m *GasPriceResponse) GetGasPrice() string { if m != nil { @@ -1218,7 +1304,7 @@ type HashRequest struct { func (m *HashRequest) Reset() { *m = HashRequest{} } func (m *HashRequest) String() string { return proto.CompactTextString(m) } func (*HashRequest) ProtoMessage() {} -func (*HashRequest) Descriptor() ([]byte, []int) { return fileDescriptorRpc, []int{33} } +func (*HashRequest) Descriptor() ([]byte, []int) { return fileDescriptorRpc, []int{35} } func (m *HashRequest) GetHash() string { if m != nil { @@ -1235,7 +1321,7 @@ type GasResponse struct { func (m *GasResponse) Reset() { *m = GasResponse{} } func (m *GasResponse) String() string { return proto.CompactTextString(m) } func (*GasResponse) ProtoMessage() {} -func (*GasResponse) Descriptor() ([]byte, []int) { return fileDescriptorRpc, []int{34} } +func (*GasResponse) Descriptor() ([]byte, []int) { return fileDescriptorRpc, []int{36} } func (m *GasResponse) GetGas() string { if m != nil { @@ -1258,7 +1344,7 @@ type EventsResponse struct { func (m *EventsResponse) Reset() { *m = EventsResponse{} } func (m *EventsResponse) String() string { return proto.CompactTextString(m) } func (*EventsResponse) ProtoMessage() {} -func (*EventsResponse) Descriptor() ([]byte, []int) { return fileDescriptorRpc, []int{35} } +func (*EventsResponse) Descriptor() ([]byte, []int) { return fileDescriptorRpc, []int{37} } func (m *EventsResponse) GetEvents() []*Event { if m != nil { @@ -1275,7 +1361,7 @@ type Event struct { func (m *Event) Reset() { *m = Event{} } func (m *Event) String() string { return proto.CompactTextString(m) } func (*Event) ProtoMessage() {} -func (*Event) Descriptor() ([]byte, []int) { return fileDescriptorRpc, []int{36} } +func (*Event) Descriptor() ([]byte, []int) { return fileDescriptorRpc, []int{38} } func (m *Event) GetTopic() string { if m != nil { @@ -1298,7 +1384,7 @@ type PprofRequest struct { func (m *PprofRequest) Reset() { *m = PprofRequest{} } func (m *PprofRequest) String() string { return proto.CompactTextString(m) } func (*PprofRequest) ProtoMessage() {} -func (*PprofRequest) Descriptor() ([]byte, []int) { return fileDescriptorRpc, []int{37} } +func (*PprofRequest) Descriptor() ([]byte, []int) { return fileDescriptorRpc, []int{39} } func (m *PprofRequest) GetListen() string { if m != nil { @@ -1314,7 +1400,7 @@ type PprofResponse struct { func (m *PprofResponse) Reset() { *m = PprofResponse{} } func (m *PprofResponse) String() string { return proto.CompactTextString(m) } func (*PprofResponse) ProtoMessage() {} -func (*PprofResponse) Descriptor() ([]byte, []int) { return fileDescriptorRpc, []int{38} } +func (*PprofResponse) Descriptor() ([]byte, []int) { return fileDescriptorRpc, []int{40} } func (m *PprofResponse) GetResult() bool { if m != nil { @@ -1331,7 +1417,7 @@ type GetConfigResponse struct { func (m *GetConfigResponse) Reset() { *m = GetConfigResponse{} } func (m *GetConfigResponse) String() string { return proto.CompactTextString(m) } func (*GetConfigResponse) ProtoMessage() {} -func (*GetConfigResponse) Descriptor() ([]byte, []int) { return fileDescriptorRpc, []int{39} } +func (*GetConfigResponse) Descriptor() ([]byte, []int) { return fileDescriptorRpc, []int{41} } func (m *GetConfigResponse) GetConfig() *nebletpb.Config { if m != nil { @@ -1370,6 +1456,8 @@ func init() { proto.RegisterType((*LockAccountResponse)(nil), "rpcpb.LockAccountResponse") proto.RegisterType((*SignHashRequest)(nil), "rpcpb.SignHashRequest") proto.RegisterType((*SignHashResponse)(nil), "rpcpb.SignHashResponse") + proto.RegisterType((*GenerateRandomSeedRequest)(nil), "rpcpb.GenerateRandomSeedRequest") + proto.RegisterType((*GenerateRandomSeedResponse)(nil), "rpcpb.GenerateRandomSeedResponse") proto.RegisterType((*SignTransactionPassphraseRequest)(nil), "rpcpb.SignTransactionPassphraseRequest") proto.RegisterType((*SignTransactionPassphraseResponse)(nil), "rpcpb.SignTransactionPassphraseResponse") proto.RegisterType((*SendTransactionPassphraseRequest)(nil), "rpcpb.SendTransactionPassphraseRequest") @@ -1916,6 +2004,7 @@ type AdminServiceClient interface { SendTransaction(ctx context.Context, in *TransactionRequest, opts ...grpc.CallOption) (*SendTransactionResponse, error) // Sign sign msg SignHash(ctx context.Context, in *SignHashRequest, opts ...grpc.CallOption) (*SignHashResponse, error) + GenerateRandomSeed(ctx context.Context, in *GenerateRandomSeedRequest, opts ...grpc.CallOption) (*GenerateRandomSeedResponse, error) // Sign sign transaction SignTransactionWithPassphrase(ctx context.Context, in *SignTransactionPassphraseRequest, opts ...grpc.CallOption) (*SignTransactionPassphraseResponse, error) // SendTransactionWithPassphrase send transaction with passphrase @@ -1989,6 +2078,15 @@ func (c *adminServiceClient) SignHash(ctx context.Context, in *SignHashRequest, return out, nil } +func (c *adminServiceClient) GenerateRandomSeed(ctx context.Context, in *GenerateRandomSeedRequest, opts ...grpc.CallOption) (*GenerateRandomSeedResponse, error) { + out := new(GenerateRandomSeedResponse) + err := grpc.Invoke(ctx, "/rpcpb.AdminService/GenerateRandomSeed", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *adminServiceClient) SignTransactionWithPassphrase(ctx context.Context, in *SignTransactionPassphraseRequest, opts ...grpc.CallOption) (*SignTransactionPassphraseResponse, error) { out := new(SignTransactionPassphraseResponse) err := grpc.Invoke(ctx, "/rpcpb.AdminService/SignTransactionWithPassphrase", in, out, c.cc, opts...) @@ -2049,6 +2147,7 @@ type AdminServiceServer interface { SendTransaction(context.Context, *TransactionRequest) (*SendTransactionResponse, error) // Sign sign msg SignHash(context.Context, *SignHashRequest) (*SignHashResponse, error) + GenerateRandomSeed(context.Context, *GenerateRandomSeedRequest) (*GenerateRandomSeedResponse, error) // Sign sign transaction SignTransactionWithPassphrase(context.Context, *SignTransactionPassphraseRequest) (*SignTransactionPassphraseResponse, error) // SendTransactionWithPassphrase send transaction with passphrase @@ -2172,6 +2271,24 @@ func _AdminService_SignHash_Handler(srv interface{}, ctx context.Context, dec fu return interceptor(ctx, in, info, handler) } +func _AdminService_GenerateRandomSeed_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GenerateRandomSeedRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AdminServiceServer).GenerateRandomSeed(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/rpcpb.AdminService/GenerateRandomSeed", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AdminServiceServer).GenerateRandomSeed(ctx, req.(*GenerateRandomSeedRequest)) + } + return interceptor(ctx, in, info, handler) +} + func _AdminService_SignTransactionWithPassphrase_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(SignTransactionPassphraseRequest) if err := dec(in); err != nil { @@ -2290,6 +2407,10 @@ var _AdminService_serviceDesc = grpc.ServiceDesc{ MethodName: "SignHash", Handler: _AdminService_SignHash_Handler, }, + { + MethodName: "GenerateRandomSeed", + Handler: _AdminService_GenerateRandomSeed_Handler, + }, { MethodName: "SignTransactionWithPassphrase", Handler: _AdminService_SignTransactionWithPassphrase_Handler, @@ -2318,142 +2439,150 @@ var _AdminService_serviceDesc = grpc.ServiceDesc{ func init() { proto.RegisterFile("rpc.proto", fileDescriptorRpc) } var fileDescriptorRpc = []byte{ - // 2190 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x59, 0x4b, 0x6f, 0x1b, 0xc9, - 0x11, 0x06, 0x49, 0x3d, 0xc8, 0x22, 0x25, 0xd1, 0xad, 0xd7, 0x88, 0x96, 0x64, 0xb9, 0xbd, 0xd8, - 0xd5, 0x1a, 0x59, 0x71, 0xad, 0x05, 0x9c, 0xc0, 0xc1, 0x06, 0xb0, 0x1d, 0xaf, 0xd6, 0x81, 0x61, - 0x38, 0x23, 0x6f, 0xb2, 0x40, 0xe2, 0x10, 0xcd, 0x61, 0x8b, 0xec, 0xec, 0x68, 0x66, 0x32, 0xdd, - 0xb4, 0x2d, 0x5d, 0x02, 0xec, 0x3d, 0xa7, 0x5c, 0x72, 0xc8, 0x3f, 0xc8, 0xaf, 0x09, 0x72, 0xc8, - 0x25, 0xc7, 0x9c, 0x72, 0xcb, 0x3f, 0x08, 0xba, 0xa6, 0x7b, 0x5e, 0x1c, 0x8a, 0x71, 0x0e, 0xb9, - 0x4d, 0x55, 0x77, 0x57, 0x55, 0xd7, 0xe3, 0xab, 0x6a, 0x12, 0x5a, 0x71, 0xe4, 0x9d, 0x44, 0x71, - 0xa8, 0x42, 0xb2, 0x1c, 0x47, 0x5e, 0x34, 0xec, 0xed, 0x8f, 0xc3, 0x70, 0xec, 0xf3, 0x3e, 0x8b, - 0x44, 0x9f, 0x05, 0x41, 0xa8, 0x98, 0x12, 0x61, 0x20, 0x93, 0x4d, 0xbd, 0x1f, 0x8d, 0x85, 0x9a, - 0x4c, 0x87, 0x27, 0x5e, 0x78, 0xd9, 0x0f, 0xf8, 0x70, 0xea, 0x33, 0x29, 0xc2, 0xfe, 0x38, 0xfc, - 0xcc, 0x10, 0x7d, 0x2f, 0x0c, 0x24, 0x0f, 0xe4, 0x54, 0xf6, 0xa3, 0x61, 0x5f, 0x2a, 0xa6, 0xb8, - 0x39, 0xf9, 0x70, 0xd1, 0xc9, 0x80, 0x0f, 0x7d, 0xae, 0xf4, 0x31, 0x2f, 0x0c, 0x2e, 0xc4, 0x38, - 0x39, 0x47, 0xef, 0x43, 0xf7, 0x7c, 0x3a, 0x94, 0x5e, 0x2c, 0x86, 0xdc, 0xe5, 0xbf, 0x9b, 0x72, - 0xa9, 0xc8, 0x0e, 0xac, 0xa8, 0x30, 0x12, 0x9e, 0x74, 0x6a, 0x47, 0x8d, 0xe3, 0x96, 0x6b, 0x28, - 0xfa, 0x25, 0xdc, 0xca, 0xed, 0x95, 0x91, 0xb6, 0x85, 0x6c, 0xc1, 0x32, 0x2e, 0x3b, 0xb5, 0xa3, - 0xda, 0x71, 0xcb, 0x4d, 0x08, 0x42, 0x60, 0x69, 0xc4, 0x14, 0x73, 0xea, 0xc8, 0xc4, 0x6f, 0x4a, - 0xa0, 0xfb, 0x32, 0x0c, 0x5e, 0xb1, 0x98, 0x5d, 0x4a, 0xa3, 0x8a, 0xfe, 0xb9, 0xae, 0x99, 0x23, - 0xfe, 0x3c, 0xb8, 0x08, 0x53, 0x91, 0xeb, 0x50, 0x17, 0x23, 0x23, 0xaf, 0x2e, 0x46, 0x64, 0x0f, - 0x9a, 0xde, 0x84, 0x89, 0x60, 0x20, 0x46, 0x28, 0x70, 0xcd, 0x5d, 0x45, 0xfa, 0xf9, 0x88, 0xf4, - 0xa0, 0xe9, 0x85, 0x22, 0x18, 0x32, 0xc9, 0x9d, 0x06, 0x1e, 0x48, 0x69, 0x72, 0x00, 0x10, 0x71, - 0x1e, 0x0f, 0xbc, 0x70, 0x1a, 0x28, 0x67, 0x09, 0x0f, 0xb6, 0x34, 0xe7, 0xa9, 0x66, 0x10, 0x0a, - 0x1d, 0x79, 0x15, 0x78, 0x93, 0x38, 0x0c, 0xc4, 0x35, 0x1f, 0x39, 0xcb, 0x47, 0xb5, 0xe3, 0xa6, - 0x5b, 0xe0, 0x91, 0x3b, 0xd0, 0x1e, 0x4e, 0xbd, 0xef, 0xb8, 0x1a, 0x48, 0x71, 0xcd, 0x9d, 0x95, - 0xa3, 0xda, 0xf1, 0xb2, 0x0b, 0x09, 0xeb, 0x5c, 0x5c, 0x73, 0xf2, 0x29, 0x74, 0xd1, 0x8f, 0x5e, - 0xe8, 0x0f, 0xde, 0xf2, 0x58, 0x8a, 0x30, 0x70, 0x00, 0xed, 0xd8, 0xb0, 0xfc, 0x5f, 0x24, 0x6c, - 0x72, 0x0a, 0xed, 0x38, 0x9c, 0x2a, 0x3e, 0x50, 0x6c, 0xe8, 0x73, 0xa7, 0x7d, 0xd4, 0x38, 0x6e, - 0x9f, 0xde, 0x3a, 0xc1, 0xb4, 0x38, 0x71, 0xf5, 0xca, 0x6b, 0xbd, 0xe0, 0x42, 0x9c, 0x7e, 0xd3, - 0x87, 0x00, 0xd9, 0xca, 0x8c, 0x5f, 0x1c, 0x58, 0x65, 0xa3, 0x51, 0xcc, 0xa5, 0x74, 0xea, 0x18, - 0x28, 0x4b, 0xd2, 0xbf, 0xd7, 0x60, 0xf3, 0x8c, 0xab, 0x97, 0x7c, 0x78, 0xae, 0x73, 0x24, 0xf5, - 0x6c, 0xde, 0x93, 0xb5, 0xa2, 0x27, 0x09, 0x2c, 0x29, 0x26, 0x7c, 0x1b, 0x31, 0xfd, 0x4d, 0xba, - 0xd0, 0xf0, 0xc5, 0xd0, 0x38, 0x56, 0x7f, 0xea, 0xd4, 0x98, 0x70, 0x31, 0x9e, 0x24, 0xfe, 0x5c, - 0x72, 0x0d, 0x55, 0xe9, 0x87, 0x95, 0x6a, 0x3f, 0x94, 0xfd, 0xbe, 0x5a, 0xe1, 0x77, 0x07, 0x56, - 0xad, 0x94, 0x26, 0x4a, 0xb1, 0x24, 0xfd, 0x1c, 0xba, 0x8f, 0x3d, 0x8c, 0xa8, 0x4c, 0x6f, 0xb5, - 0x0f, 0x2d, 0x73, 0x71, 0x6e, 0x53, 0x36, 0x63, 0xd0, 0x9f, 0xc1, 0xce, 0x19, 0x57, 0xe6, 0x90, - 0x71, 0x47, 0x92, 0xe7, 0x39, 0xff, 0x25, 0x4e, 0xb5, 0x64, 0xee, 0x9a, 0xf5, 0xfc, 0x35, 0xe9, - 0x1b, 0xd8, 0x9d, 0x91, 0x65, 0x8c, 0x70, 0x60, 0x75, 0xc8, 0x7c, 0x16, 0x78, 0xdc, 0x0a, 0x33, - 0xa4, 0xae, 0x90, 0x20, 0xd4, 0xfc, 0x44, 0x56, 0x42, 0xa0, 0xbf, 0xaf, 0xa2, 0x24, 0x6b, 0xd7, - 0x5c, 0xfc, 0xa6, 0xbf, 0x85, 0xce, 0x53, 0xe6, 0xfb, 0xa9, 0xcc, 0x1d, 0x58, 0x89, 0xb9, 0x9c, - 0xfa, 0xca, 0x88, 0x34, 0x94, 0x4e, 0x4b, 0xfe, 0x9e, 0x7b, 0x3a, 0x99, 0x78, 0x1c, 0x9b, 0x90, - 0x81, 0x61, 0x3d, 0x8b, 0x63, 0x72, 0x17, 0x3a, 0x5c, 0x2a, 0x71, 0xc9, 0x14, 0x1f, 0x8c, 0x99, - 0x34, 0x11, 0x6c, 0x5b, 0xde, 0x19, 0x93, 0xf4, 0x04, 0xb6, 0x9e, 0x5c, 0x3d, 0xf1, 0x43, 0xef, - 0xbb, 0xaf, 0xf1, 0x6e, 0xb9, 0xe2, 0x37, 0x57, 0xaf, 0x15, 0xae, 0xfe, 0x03, 0x20, 0x67, 0x5c, - 0xfd, 0xf4, 0x2a, 0x60, 0x52, 0x5d, 0xe5, 0x2d, 0xbc, 0x14, 0x01, 0x8f, 0x53, 0xa8, 0x48, 0x28, - 0xfa, 0xaf, 0x1a, 0x90, 0xd7, 0x31, 0x0b, 0x24, 0xf3, 0x34, 0xbe, 0x59, 0xe1, 0x04, 0x96, 0x2e, - 0xe2, 0xf0, 0xd2, 0x5c, 0x07, 0xbf, 0x75, 0x56, 0xab, 0xd0, 0xdc, 0xa1, 0xae, 0x42, 0xed, 0xae, - 0xb7, 0xcc, 0x9f, 0xda, 0x7a, 0x4e, 0x88, 0xcc, 0x89, 0x4b, 0x79, 0x27, 0xde, 0x86, 0xd6, 0x98, - 0xc9, 0x41, 0x14, 0x0b, 0x8f, 0x63, 0x01, 0xb7, 0xdc, 0xe6, 0x98, 0xc9, 0x57, 0x9a, 0xb6, 0x8b, - 0xbe, 0xb8, 0x14, 0xca, 0x24, 0xa3, 0x5e, 0x7c, 0xa1, 0x69, 0x72, 0xaa, 0x81, 0x23, 0x50, 0x31, - 0xf3, 0x14, 0x66, 0x60, 0xfb, 0x74, 0xc7, 0x94, 0xe2, 0x53, 0xc3, 0x36, 0x36, 0xbb, 0xe9, 0x3e, - 0x7d, 0xd9, 0xa1, 0x08, 0x58, 0x7c, 0x85, 0x25, 0xde, 0x71, 0x0d, 0x45, 0xaf, 0x61, 0xa3, 0x74, - 0x48, 0x6f, 0x95, 0xe1, 0x34, 0x4e, 0x93, 0xc1, 0x50, 0x3a, 0x72, 0xc9, 0xd7, 0x00, 0x83, 0x6f, - 0x22, 0x97, 0xb0, 0x5e, 0x5f, 0x45, 0x5c, 0x03, 0xda, 0xc5, 0x34, 0x40, 0xa7, 0x59, 0x40, 0xb3, - 0xb4, 0xf6, 0x1e, 0x8b, 0xc7, 0x12, 0x5d, 0xd0, 0x72, 0xf1, 0x9b, 0xf6, 0x61, 0xef, 0x9c, 0x07, - 0x23, 0x97, 0xbd, 0xab, 0x76, 0x37, 0xa2, 0x70, 0x0d, 0xcd, 0x4d, 0x50, 0xf8, 0xd7, 0xb0, 0xab, - 0x0f, 0x14, 0x76, 0x67, 0xc1, 0x54, 0xef, 0x27, 0x4c, 0x4e, 0xac, 0xd1, 0x09, 0xa5, 0x8b, 0xdb, - 0xfa, 0x60, 0x90, 0x01, 0x0e, 0x16, 0xb7, 0xe5, 0x3f, 0x36, 0xc0, 0x33, 0x80, 0xed, 0x33, 0xae, - 0x30, 0xad, 0x9e, 0x5c, 0x7d, 0xcd, 0xe4, 0x24, 0x67, 0x4a, 0x4e, 0x32, 0x7e, 0x93, 0x53, 0xd8, - 0xbe, 0x98, 0xfa, 0xfe, 0xe0, 0x42, 0xf8, 0xfe, 0x40, 0x65, 0x06, 0xa1, 0xf0, 0xa6, 0xbb, 0xa9, - 0x17, 0xbf, 0x12, 0xbe, 0x9f, 0xb3, 0x95, 0x72, 0xac, 0x40, 0xab, 0xe0, 0xbf, 0xc9, 0xdc, 0xff, - 0x49, 0xcd, 0x03, 0xb8, 0x7d, 0xc6, 0x55, 0x8e, 0xb3, 0xf0, 0x36, 0xf4, 0x1f, 0x0d, 0x58, 0x43, - 0xbb, 0x52, 0x7f, 0x56, 0xdd, 0xf9, 0x0e, 0xb4, 0x23, 0x16, 0xf3, 0x40, 0x0d, 0x70, 0xc9, 0x24, - 0x40, 0xc2, 0xd2, 0x1a, 0x72, 0xb7, 0x68, 0x14, 0x6e, 0x51, 0x5d, 0x00, 0xf9, 0xfe, 0xb7, 0x5c, - 0xea, 0x7f, 0xfb, 0xd0, 0x52, 0xe2, 0x92, 0x4b, 0xc5, 0x2e, 0x23, 0xcc, 0xff, 0x86, 0x9b, 0x31, - 0x0a, 0xad, 0x60, 0xb5, 0xd8, 0x0a, 0x0e, 0x00, 0x70, 0xb4, 0x18, 0xc4, 0x61, 0xa8, 0x0c, 0x00, - 0xb7, 0x90, 0xe3, 0x86, 0xa1, 0xd2, 0x27, 0xd5, 0x7b, 0x99, 0x2c, 0xb6, 0x12, 0xa8, 0x53, 0xef, - 0x25, 0x2e, 0x69, 0x60, 0x7a, 0xcb, 0x03, 0x65, 0x56, 0xc1, 0x00, 0x13, 0xb2, 0x70, 0xc3, 0x63, - 0x58, 0x4f, 0x47, 0x98, 0x64, 0x4f, 0x1b, 0x8b, 0xaf, 0x77, 0x92, 0xb2, 0x93, 0x12, 0x4c, 0xbe, - 0xf5, 0x19, 0x77, 0xcd, 0xcb, 0x93, 0xda, 0x11, 0x08, 0x32, 0x4e, 0x27, 0xc1, 0x07, 0x24, 0xb4, - 0x66, 0x21, 0x07, 0x17, 0x22, 0x60, 0xbe, 0x50, 0x57, 0xce, 0x1a, 0x86, 0x16, 0x84, 0xfc, 0xca, - 0x70, 0xc8, 0x4f, 0xa0, 0x93, 0x8b, 0xbd, 0x74, 0x46, 0xd8, 0x7f, 0x7b, 0xa6, 0xe8, 0x2b, 0xca, - 0xc1, 0x2d, 0xec, 0xa7, 0xff, 0xae, 0xc3, 0x66, 0x55, 0xd1, 0x54, 0x05, 0xd9, 0x01, 0xeb, 0xcb, - 0xf2, 0xbc, 0x62, 0x01, 0xb0, 0x31, 0x03, 0x80, 0x4b, 0xb3, 0x00, 0xb8, 0x5c, 0x09, 0x80, 0x2b, - 0xf9, 0xf8, 0x17, 0x62, 0xbc, 0x5a, 0x8e, 0xb1, 0xed, 0x31, 0x4d, 0xd3, 0xd3, 0x35, 0xc0, 0x58, - 0x4c, 0x68, 0x65, 0x98, 0x50, 0x84, 0x51, 0xb8, 0x09, 0x46, 0xdb, 0x25, 0x18, 0xad, 0x82, 0x86, - 0x4e, 0x25, 0x34, 0x20, 0x24, 0x2a, 0xa6, 0xa6, 0x12, 0x83, 0xb3, 0xec, 0x1a, 0x4a, 0xa7, 0x93, - 0x96, 0x3f, 0x95, 0x7c, 0xe4, 0xac, 0x27, 0xe9, 0x34, 0x66, 0xf2, 0x1b, 0xc9, 0x47, 0xf4, 0x0b, - 0xb8, 0xf5, 0x92, 0xbf, 0x33, 0xed, 0xd6, 0xd6, 0xde, 0x21, 0x40, 0xc4, 0xa4, 0x8c, 0x26, 0xb1, - 0x4e, 0xfa, 0x9a, 0x2d, 0x20, 0xcb, 0xa1, 0x27, 0x40, 0xf2, 0x87, 0xb2, 0xf6, 0x5c, 0xdd, 0xeb, - 0xa9, 0x0f, 0x5b, 0xdf, 0x04, 0xba, 0x6e, 0x4b, 0x7a, 0xe6, 0x4f, 0x07, 0x45, 0x0b, 0xea, 0x65, - 0x0b, 0x74, 0x51, 0x8e, 0xa6, 0x31, 0x4b, 0x31, 0x7c, 0xc9, 0x4d, 0x69, 0xda, 0x87, 0xed, 0x92, - 0xb6, 0xca, 0x5e, 0xdf, 0xb4, 0xbd, 0x5e, 0x5f, 0xe7, 0xc5, 0x07, 0x18, 0x47, 0x3f, 0x83, 0xcd, - 0x17, 0x1f, 0x20, 0xfe, 0xe7, 0xb0, 0x71, 0x2e, 0xc6, 0x41, 0x1e, 0xdc, 0xe6, 0x5f, 0xdc, 0xe6, - 0x7a, 0x3d, 0xc9, 0x1d, 0xcc, 0xf5, 0x2e, 0x34, 0x98, 0x3f, 0x36, 0x63, 0x8c, 0xfe, 0xa4, 0x1f, - 0x43, 0x37, 0x13, 0x99, 0x55, 0xc9, 0x4c, 0x27, 0xfa, 0x3d, 0x1c, 0xe9, 0x7d, 0xb9, 0xa2, 0x7a, - 0x95, 0xfa, 0xd0, 0xda, 0xf2, 0x63, 0x68, 0xe7, 0x11, 0xbb, 0x86, 0x60, 0xb1, 0x57, 0x55, 0xb4, - 0x49, 0xb3, 0xce, 0xef, 0x5e, 0x14, 0x27, 0xfa, 0x43, 0xb8, 0x7b, 0x83, 0x01, 0x0b, 0x2c, 0x2f, - 0xf6, 0xd0, 0xff, 0xb3, 0xe5, 0x7d, 0xe8, 0x9e, 0x99, 0xfa, 0x4c, 0x0d, 0x2d, 0x14, 0x71, 0xad, - 0x58, 0xc4, 0xf4, 0x2e, 0xb4, 0x17, 0xf5, 0xaf, 0x07, 0xd0, 0x3e, 0x63, 0xd9, 0x50, 0xdd, 0x85, - 0x86, 0x9e, 0x1c, 0x93, 0x1d, 0xfa, 0x53, 0x73, 0xb2, 0x69, 0x53, 0x7f, 0xd2, 0x87, 0xb0, 0xfe, - 0x2c, 0xc1, 0x76, 0x7b, 0xea, 0x23, 0x58, 0x49, 0xd0, 0x1e, 0xe7, 0xc1, 0xf6, 0x69, 0xc7, 0x5c, - 0x18, 0xb7, 0xb9, 0x66, 0x8d, 0x3e, 0x80, 0x65, 0x64, 0x7c, 0xc0, 0xe3, 0xf1, 0x63, 0xe8, 0xbc, - 0x8a, 0xe2, 0xf0, 0x22, 0xd7, 0xec, 0x7d, 0x21, 0x15, 0x0f, 0xec, 0xac, 0x92, 0x50, 0xf4, 0x13, - 0x58, 0x33, 0xfb, 0x16, 0x24, 0xfe, 0x97, 0x70, 0xeb, 0x8c, 0xab, 0xa7, 0xf8, 0x16, 0x4e, 0x37, - 0x1f, 0xc3, 0x4a, 0xf2, 0x3a, 0x36, 0xf1, 0xea, 0x9e, 0x24, 0xcf, 0xe6, 0xa4, 0x27, 0xe9, 0x9d, - 0x66, 0xfd, 0xf4, 0xaf, 0x00, 0xf0, 0x38, 0x12, 0xe7, 0x3c, 0x7e, 0xab, 0x41, 0xf2, 0x0d, 0xb4, - 0x73, 0xef, 0x2d, 0xb2, 0x6b, 0xae, 0x5d, 0x7e, 0xef, 0xf6, 0x6c, 0xbf, 0xa9, 0x78, 0x9c, 0xd1, - 0xbd, 0xef, 0xff, 0xf6, 0xcf, 0x3f, 0xd6, 0x37, 0xc9, 0xad, 0xfe, 0xdb, 0x07, 0xfd, 0xa9, 0xe4, - 0xb1, 0x7e, 0xb3, 0x63, 0xdb, 0x25, 0xbf, 0x81, 0xdd, 0x17, 0x4c, 0x71, 0xa9, 0x9e, 0xc7, 0x31, - 0xc7, 0xa7, 0xd0, 0xd0, 0xe7, 0x38, 0x6c, 0xcc, 0x57, 0xb5, 0x65, 0x16, 0x0a, 0x33, 0x09, 0xdd, - 0x42, 0x25, 0xeb, 0xa4, 0x93, 0x2a, 0xd1, 0xcf, 0xba, 0x18, 0x36, 0x4a, 0xef, 0x1a, 0x72, 0x90, - 0x59, 0x5a, 0xf1, 0x76, 0xea, 0x1d, 0xce, 0x5b, 0x36, 0x7a, 0x8e, 0x50, 0x4f, 0x8f, 0x6e, 0xa7, - 0x7a, 0x98, 0x79, 0xb6, 0xe9, 0x6d, 0x8f, 0x6a, 0xf7, 0xc9, 0x2b, 0x58, 0xd2, 0x8f, 0x1d, 0x32, - 0xbf, 0x26, 0x7a, 0x9b, 0x76, 0x24, 0xcf, 0x3d, 0x8a, 0xa8, 0x83, 0x92, 0x09, 0x5d, 0x4b, 0x25, - 0x7b, 0xcc, 0xf7, 0xb5, 0xc4, 0x6b, 0x20, 0xb3, 0xb3, 0x30, 0x39, 0x32, 0x42, 0xe6, 0x8e, 0xc9, - 0xe9, 0x5d, 0xe6, 0xcc, 0xc5, 0x94, 0xa2, 0xc6, 0x7d, 0xba, 0x9b, 0x6a, 0x8c, 0xd9, 0xbb, 0x5c, - 0xb9, 0x6a, 0xdd, 0x13, 0x58, 0x2f, 0x0e, 0xbe, 0x64, 0x3f, 0xf3, 0xd0, 0xec, 0x3c, 0x3c, 0x27, - 0x3a, 0xb3, 0x9a, 0xc6, 0x85, 0xd3, 0x5a, 0x53, 0x00, 0xdd, 0xf2, 0x04, 0x4c, 0x0e, 0x67, 0x75, - 0xe5, 0x47, 0xe3, 0x39, 0xda, 0x3e, 0x42, 0x6d, 0x87, 0x74, 0xaf, 0x4a, 0x1b, 0x9e, 0xd7, 0xfa, - 0xbe, 0xaf, 0xe1, 0x4c, 0x5f, 0x70, 0x8c, 0xc7, 0x45, 0xa4, 0x08, 0xcd, 0xb4, 0xce, 0x9b, 0x94, - 0x7b, 0x37, 0x0c, 0x58, 0xf4, 0x53, 0xd4, 0x7f, 0x8f, 0x1e, 0xe6, 0xf5, 0xcf, 0xea, 0xd1, 0x46, - 0x0c, 0xa0, 0x95, 0xfe, 0xf4, 0x94, 0xa6, 0x7c, 0xf9, 0x87, 0xab, 0x9e, 0x33, 0xbb, 0x60, 0x54, - 0x1d, 0xa0, 0xaa, 0x5d, 0x4a, 0x52, 0x55, 0xd2, 0xee, 0x79, 0x54, 0xbb, 0xff, 0x79, 0xcd, 0x14, - 0xb0, 0x05, 0xd5, 0xf9, 0x55, 0x65, 0x17, 0xca, 0xf0, 0x4b, 0xf7, 0x51, 0xc3, 0x0e, 0xd9, 0xca, - 0x5f, 0x26, 0x95, 0xf7, 0x06, 0xda, 0xcf, 0xb2, 0xc7, 0xf7, 0x4d, 0x39, 0x4f, 0x32, 0x05, 0xa9, - 0xec, 0x3b, 0x28, 0x7b, 0x8f, 0x66, 0xb2, 0x73, 0x2f, 0x79, 0xed, 0x1e, 0x86, 0xf5, 0x9b, 0x60, - 0xb1, 0x49, 0x3f, 0x2b, 0x27, 0x1f, 0x8c, 0xed, 0x3c, 0x1a, 0x67, 0xe2, 0xef, 0xa1, 0xf8, 0x03, - 0xea, 0xe4, 0x4d, 0xcf, 0x0b, 0x4b, 0x54, 0x40, 0xf6, 0xfe, 0x27, 0xb7, 0x6d, 0x42, 0x55, 0xfc, - 0x84, 0xd0, 0xdb, 0xcb, 0xf2, 0xa2, 0xf4, 0x7b, 0x01, 0xbd, 0x8d, 0xaa, 0xb6, 0x69, 0x37, 0x55, - 0x35, 0x4a, 0x76, 0x3c, 0xaa, 0xdd, 0x3f, 0xfd, 0x4b, 0x0b, 0x3a, 0x8f, 0x47, 0x97, 0x22, 0xb0, - 0xa8, 0xfa, 0x2d, 0x34, 0xed, 0x8f, 0x3d, 0x8b, 0x23, 0x52, 0xfe, 0x59, 0x88, 0xf6, 0x50, 0xd7, - 0x16, 0xc1, 0x98, 0x33, 0x2d, 0x37, 0xc5, 0x20, 0xe2, 0x01, 0x64, 0x43, 0x22, 0xb1, 0x79, 0x33, - 0x33, 0x6c, 0xa6, 0x57, 0x99, 0x9d, 0x28, 0x8b, 0x08, 0x57, 0x10, 0xdf, 0x0f, 0xf8, 0x3b, 0xed, - 0xb2, 0x10, 0xd6, 0x0a, 0xb3, 0x5e, 0xea, 0xb5, 0xaa, 0x79, 0xb3, 0xb7, 0x5f, 0xbd, 0x58, 0x15, - 0xa3, 0xa2, 0xb6, 0x29, 0x1e, 0xd0, 0x0a, 0xc7, 0xd0, 0xce, 0xcd, 0x7e, 0x69, 0x96, 0xcd, 0xce, - 0x8f, 0x69, 0x59, 0x56, 0x8c, 0x8a, 0xf4, 0x2e, 0xaa, 0xba, 0x4d, 0x77, 0x66, 0x55, 0x59, 0x45, - 0x01, 0x6c, 0x94, 0xc0, 0xf2, 0xa6, 0x94, 0x5e, 0x84, 0xaf, 0x15, 0x9e, 0x2c, 0xa1, 0xeb, 0xaf, - 0xa0, 0x69, 0x47, 0x4a, 0x62, 0x7f, 0xa7, 0x29, 0x8d, 0xad, 0x69, 0x1e, 0x94, 0x67, 0x4f, 0x7a, - 0x88, 0xe2, 0x1d, 0xba, 0x99, 0x89, 0x97, 0x62, 0x1c, 0xf4, 0x27, 0x26, 0xb3, 0xff, 0x50, 0x83, - 0x83, 0xd2, 0x1c, 0xf8, 0x4b, 0xa1, 0x26, 0xd9, 0x48, 0x47, 0x3e, 0xc9, 0x89, 0xbe, 0x69, 0xe8, - 0xeb, 0x1d, 0x2f, 0xde, 0x58, 0x6c, 0xf6, 0x74, 0xbd, 0x68, 0x94, 0xb6, 0xe7, 0x4f, 0xda, 0x9e, - 0xa2, 0xab, 0xe6, 0xd9, 0xb3, 0x60, 0x08, 0x5d, 0xe8, 0xf9, 0x13, 0xb4, 0xe2, 0x98, 0xde, 0xab, - 0xf4, 0x7c, 0x51, 0xab, 0x36, 0xed, 0x1c, 0xe0, 0x5c, 0xb1, 0x58, 0xe1, 0x88, 0x45, 0x6c, 0x7b, - 0xce, 0x0f, 0x66, 0x69, 0xab, 0x29, 0x4c, 0x61, 0xb6, 0x16, 0xe9, 0x46, 0xa6, 0x28, 0xd2, 0x1b, - 0x92, 0xe0, 0xb6, 0xd2, 0x49, 0x6c, 0x7e, 0x99, 0x3b, 0x19, 0xa8, 0x14, 0x87, 0x36, 0x8b, 0x29, - 0x24, 0x17, 0xdf, 0x71, 0x2a, 0xef, 0x5b, 0x68, 0xda, 0xff, 0x17, 0x16, 0x43, 0x48, 0xf9, 0x9f, - 0x88, 0x2a, 0x08, 0x09, 0xc2, 0x11, 0x17, 0xc1, 0x45, 0x38, 0x5c, 0xc1, 0x1f, 0xb6, 0xbf, 0xf8, - 0x4f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x97, 0x9c, 0x43, 0x6f, 0xe4, 0x19, 0x00, 0x00, + // 2320 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x59, 0x4f, 0x6f, 0x1b, 0xb9, + 0x15, 0x87, 0x2c, 0xff, 0x91, 0x9e, 0x64, 0x5b, 0xa1, 0xff, 0x8d, 0x15, 0xdb, 0x6b, 0x33, 0xdb, + 0x8d, 0x37, 0xe8, 0x5a, 0x1b, 0x2f, 0x90, 0x16, 0x29, 0xb6, 0x40, 0x92, 0x66, 0xbd, 0x29, 0x82, + 0xc0, 0x1d, 0x67, 0xdb, 0x05, 0xda, 0x54, 0xa0, 0x66, 0x28, 0x69, 0xba, 0x63, 0xce, 0x94, 0xa4, + 0x9c, 0xd8, 0x97, 0x02, 0xb9, 0xf7, 0xd4, 0x4b, 0x0f, 0xfd, 0x10, 0xfd, 0x02, 0xfd, 0x10, 0x45, + 0x0f, 0xbd, 0xf4, 0xd8, 0xcf, 0x51, 0x14, 0xe4, 0x90, 0xf3, 0x4f, 0x23, 0x6b, 0xd3, 0x43, 0x6f, + 0xf3, 0x1e, 0xc9, 0xf7, 0x1e, 0xdf, 0x9f, 0x1f, 0x1f, 0x39, 0xd0, 0xe4, 0xb1, 0x77, 0x12, 0xf3, + 0x48, 0x46, 0x68, 0x89, 0xc7, 0x5e, 0x3c, 0xe8, 0xee, 0x8d, 0xa2, 0x68, 0x14, 0xd2, 0x1e, 0x89, + 0x83, 0x1e, 0x61, 0x2c, 0x92, 0x44, 0x06, 0x11, 0x13, 0xc9, 0xa4, 0xee, 0x8f, 0x47, 0x81, 0x1c, + 0x4f, 0x06, 0x27, 0x5e, 0x74, 0xd9, 0x63, 0x74, 0x30, 0x09, 0x89, 0x08, 0xa2, 0xde, 0x28, 0xfa, + 0xcc, 0x10, 0x3d, 0x2f, 0x62, 0x82, 0x32, 0x31, 0x11, 0xbd, 0x78, 0xd0, 0x13, 0x92, 0x48, 0x6a, + 0x56, 0x3e, 0x9a, 0xb7, 0x92, 0xd1, 0x41, 0x48, 0xa5, 0x5a, 0xe6, 0x45, 0x6c, 0x18, 0x8c, 0x92, + 0x75, 0xf8, 0x01, 0x74, 0x2e, 0x26, 0x03, 0xe1, 0xf1, 0x60, 0x40, 0x5d, 0xfa, 0xfb, 0x09, 0x15, + 0x12, 0x6d, 0xc3, 0xb2, 0x8c, 0xe2, 0xc0, 0x13, 0x4e, 0xed, 0xb0, 0x7e, 0xdc, 0x74, 0x0d, 0x85, + 0xbf, 0x84, 0x3b, 0xb9, 0xb9, 0x22, 0x56, 0xb6, 0xa0, 0x4d, 0x58, 0xd2, 0xc3, 0x4e, 0xed, 0xb0, + 0x76, 0xdc, 0x74, 0x13, 0x02, 0x21, 0x58, 0xf4, 0x89, 0x24, 0xce, 0x82, 0x66, 0xea, 0x6f, 0x8c, + 0xa0, 0xf3, 0x2a, 0x62, 0xe7, 0x84, 0x93, 0x4b, 0x61, 0x54, 0xe1, 0xbf, 0x2c, 0x28, 0xa6, 0x4f, + 0x5f, 0xb0, 0x61, 0x94, 0x8a, 0x5c, 0x83, 0x85, 0xc0, 0x37, 0xf2, 0x16, 0x02, 0x1f, 0xed, 0x42, + 0xc3, 0x1b, 0x93, 0x80, 0xf5, 0x03, 0x5f, 0x0b, 0x5c, 0x75, 0x57, 0x34, 0xfd, 0xc2, 0x47, 0x5d, + 0x68, 0x78, 0x51, 0xc0, 0x06, 0x44, 0x50, 0xa7, 0xae, 0x17, 0xa4, 0x34, 0xda, 0x07, 0x88, 0x29, + 0xe5, 0x7d, 0x2f, 0x9a, 0x30, 0xe9, 0x2c, 0xea, 0x85, 0x4d, 0xc5, 0x79, 0xa6, 0x18, 0x08, 0x43, + 0x5b, 0x5c, 0x33, 0x6f, 0xcc, 0x23, 0x16, 0xdc, 0x50, 0xdf, 0x59, 0x3a, 0xac, 0x1d, 0x37, 0xdc, + 0x02, 0x0f, 0x7d, 0x04, 0xad, 0xc1, 0xc4, 0xfb, 0x8e, 0xca, 0xbe, 0x08, 0x6e, 0xa8, 0xb3, 0x7c, + 0x58, 0x3b, 0x5e, 0x72, 0x21, 0x61, 0x5d, 0x04, 0x37, 0x14, 0x7d, 0x0a, 0x1d, 0xed, 0x47, 0x2f, + 0x0a, 0xfb, 0x57, 0x94, 0x8b, 0x20, 0x62, 0x0e, 0x68, 0x3b, 0xd6, 0x2d, 0xff, 0x97, 0x09, 0x1b, + 0x9d, 0x42, 0x8b, 0x47, 0x13, 0x49, 0xfb, 0x92, 0x0c, 0x42, 0xea, 0xb4, 0x0e, 0xeb, 0xc7, 0xad, + 0xd3, 0x3b, 0x27, 0x3a, 0x2d, 0x4e, 0x5c, 0x35, 0xf2, 0x5a, 0x0d, 0xb8, 0xc0, 0xd3, 0x6f, 0xfc, + 0x08, 0x20, 0x1b, 0x99, 0xf2, 0x8b, 0x03, 0x2b, 0xc4, 0xf7, 0x39, 0x15, 0xc2, 0x59, 0xd0, 0x81, + 0xb2, 0x24, 0xfe, 0x67, 0x0d, 0x36, 0xce, 0xa8, 0x7c, 0x45, 0x07, 0x17, 0x2a, 0x47, 0x52, 0xcf, + 0xe6, 0x3d, 0x59, 0x2b, 0x7a, 0x12, 0xc1, 0xa2, 0x24, 0x41, 0x68, 0x23, 0xa6, 0xbe, 0x51, 0x07, + 0xea, 0x61, 0x30, 0x30, 0x8e, 0x55, 0x9f, 0x2a, 0x35, 0xc6, 0x34, 0x18, 0x8d, 0x13, 0x7f, 0x2e, + 0xba, 0x86, 0xaa, 0xf4, 0xc3, 0x72, 0xb5, 0x1f, 0xca, 0x7e, 0x5f, 0xa9, 0xf0, 0xbb, 0x03, 0x2b, + 0x56, 0x4a, 0x43, 0x4b, 0xb1, 0x24, 0xfe, 0x1c, 0x3a, 0x4f, 0x3c, 0x1d, 0x51, 0x91, 0xee, 0x6a, + 0x0f, 0x9a, 0x66, 0xe3, 0xd4, 0xa6, 0x6c, 0xc6, 0xc0, 0x3f, 0x87, 0xed, 0x33, 0x2a, 0xcd, 0x22, + 0xe3, 0x8e, 0x24, 0xcf, 0x73, 0xfe, 0x4b, 0x9c, 0x6a, 0xc9, 0xdc, 0x36, 0x17, 0xf2, 0xdb, 0xc4, + 0x6f, 0x60, 0x67, 0x4a, 0x96, 0x31, 0xc2, 0x81, 0x95, 0x01, 0x09, 0x09, 0xf3, 0xa8, 0x15, 0x66, + 0x48, 0x55, 0x21, 0x2c, 0x52, 0xfc, 0x44, 0x56, 0x42, 0x68, 0x7f, 0x5f, 0xc7, 0x49, 0xd6, 0xae, + 0xba, 0xfa, 0x1b, 0xff, 0x0e, 0xda, 0xcf, 0x48, 0x18, 0xa6, 0x32, 0xb7, 0x61, 0x99, 0x53, 0x31, + 0x09, 0xa5, 0x11, 0x69, 0x28, 0x95, 0x96, 0xf4, 0x1d, 0xf5, 0x54, 0x32, 0x51, 0xce, 0x4d, 0xc8, + 0xc0, 0xb0, 0x9e, 0x73, 0x8e, 0x8e, 0xa0, 0x4d, 0x85, 0x0c, 0x2e, 0x89, 0xa4, 0xfd, 0x11, 0x11, + 0x26, 0x82, 0x2d, 0xcb, 0x3b, 0x23, 0x02, 0x9f, 0xc0, 0xe6, 0xd3, 0xeb, 0xa7, 0x61, 0xe4, 0x7d, + 0xf7, 0xb5, 0xde, 0x5b, 0xae, 0xf8, 0xcd, 0xd6, 0x6b, 0x85, 0xad, 0xff, 0x10, 0xd0, 0x19, 0x95, + 0x3f, 0xbb, 0x66, 0x44, 0xc8, 0xeb, 0xbc, 0x85, 0x97, 0x01, 0xa3, 0x3c, 0x85, 0x8a, 0x84, 0xc2, + 0xff, 0xa9, 0x01, 0x7a, 0xcd, 0x09, 0x13, 0xc4, 0x53, 0xf8, 0x66, 0x85, 0x23, 0x58, 0x1c, 0xf2, + 0xe8, 0xd2, 0x6c, 0x47, 0x7f, 0xab, 0xac, 0x96, 0x91, 0xd9, 0xc3, 0x82, 0x8c, 0x94, 0xbb, 0xae, + 0x48, 0x38, 0xb1, 0xf5, 0x9c, 0x10, 0x99, 0x13, 0x17, 0xf3, 0x4e, 0xbc, 0x0b, 0xcd, 0x11, 0x11, + 0xfd, 0x98, 0x07, 0x1e, 0xd5, 0x05, 0xdc, 0x74, 0x1b, 0x23, 0x22, 0xce, 0x15, 0x6d, 0x07, 0xc3, + 0xe0, 0x32, 0x90, 0x26, 0x19, 0xd5, 0xe0, 0x4b, 0x45, 0xa3, 0x53, 0x05, 0x1c, 0x4c, 0x72, 0xe2, + 0x49, 0x9d, 0x81, 0xad, 0xd3, 0x6d, 0x53, 0x8a, 0xcf, 0x0c, 0xdb, 0xd8, 0xec, 0xa6, 0xf3, 0xd4, + 0x66, 0x07, 0x01, 0x23, 0xfc, 0x5a, 0x97, 0x78, 0xdb, 0x35, 0x54, 0x1a, 0xca, 0x4d, 0x53, 0x3a, + 0x2a, 0x94, 0x37, 0xb0, 0x5e, 0x12, 0xa4, 0x96, 0x8b, 0x68, 0xc2, 0xd3, 0x04, 0x31, 0x94, 0x8a, + 0x66, 0xf2, 0xd5, 0xd7, 0x52, 0x4c, 0x34, 0x13, 0xd6, 0xeb, 0xeb, 0x98, 0x2a, 0x90, 0x1b, 0x4e, + 0x98, 0x76, 0xa4, 0x05, 0x39, 0x4b, 0x2b, 0xdd, 0x84, 0x8f, 0x84, 0x76, 0x4b, 0xd3, 0xd5, 0xdf, + 0xb8, 0x07, 0xbb, 0x17, 0x94, 0xf9, 0x2e, 0x79, 0x5b, 0x1d, 0x02, 0x8d, 0xcc, 0x35, 0xbd, 0x85, + 0x04, 0x99, 0x7f, 0x03, 0x3b, 0x6a, 0x41, 0x61, 0x76, 0x16, 0x60, 0xf9, 0x6e, 0x4c, 0xc4, 0xd8, + 0x1a, 0x9d, 0x50, 0xaa, 0xe0, 0xad, 0x5f, 0xfa, 0x19, 0x08, 0xe9, 0x82, 0xb7, 0xfc, 0x27, 0x06, + 0x8c, 0xfa, 0xb0, 0x75, 0x46, 0xa5, 0x4e, 0xb5, 0xa7, 0xd7, 0x5f, 0x13, 0x31, 0xce, 0x99, 0x92, + 0x93, 0xac, 0xbf, 0xd1, 0x29, 0x6c, 0x0d, 0x27, 0x61, 0xd8, 0x1f, 0x06, 0x61, 0xd8, 0x97, 0x99, + 0x41, 0x5a, 0x78, 0xc3, 0xdd, 0x50, 0x83, 0x5f, 0x05, 0x61, 0x98, 0xb3, 0x15, 0x53, 0x5d, 0x95, + 0x56, 0xc1, 0xf7, 0xc9, 0xe6, 0xff, 0x49, 0xcd, 0x43, 0xb8, 0x7b, 0x46, 0x65, 0x8e, 0x33, 0x77, + 0x37, 0xf8, 0x5f, 0x75, 0x58, 0xd5, 0x76, 0xa5, 0xfe, 0xac, 0xda, 0xf3, 0x47, 0xd0, 0x8a, 0x09, + 0xa7, 0x4c, 0xf6, 0xf5, 0x90, 0x49, 0x80, 0x84, 0xa5, 0x34, 0xe4, 0x76, 0x51, 0x2f, 0xec, 0xa2, + 0xba, 0x28, 0xf2, 0x67, 0xe2, 0x52, 0xe9, 0x4c, 0xdc, 0x83, 0xa6, 0x0c, 0x2e, 0xa9, 0x90, 0xe4, + 0x32, 0xd6, 0x35, 0x51, 0x77, 0x33, 0x46, 0xe1, 0x78, 0x58, 0x29, 0x1e, 0x0f, 0xfb, 0x00, 0xba, + 0xdd, 0xe8, 0xf3, 0x28, 0x92, 0x06, 0x94, 0x9b, 0x9a, 0xe3, 0x46, 0x91, 0x54, 0x2b, 0xe5, 0x3b, + 0x91, 0x0c, 0x36, 0x13, 0xf8, 0x93, 0xef, 0x84, 0x1e, 0x52, 0x60, 0x75, 0x45, 0x99, 0x34, 0xa3, + 0x60, 0xc0, 0x4a, 0xb3, 0xf4, 0x84, 0x27, 0xb0, 0x96, 0xb6, 0x35, 0xc9, 0x9c, 0x96, 0x2e, 0xc8, + 0xee, 0x49, 0xca, 0x4e, 0xca, 0x32, 0xf9, 0x56, 0x6b, 0xdc, 0x55, 0x2f, 0x4f, 0x2a, 0x47, 0x68, + 0xe0, 0x71, 0xda, 0x09, 0x66, 0x68, 0x42, 0x69, 0x0e, 0x44, 0x7f, 0x18, 0x30, 0x12, 0x06, 0xf2, + 0xda, 0x59, 0xd5, 0xa1, 0x85, 0x40, 0x7c, 0x65, 0x38, 0xe8, 0xa7, 0xd0, 0xce, 0xc5, 0x5e, 0x38, + 0xbe, 0x3e, 0x93, 0xbb, 0x06, 0x08, 0x2a, 0xca, 0xc1, 0x2d, 0xcc, 0xc7, 0x7f, 0xab, 0xc3, 0x46, + 0x55, 0xd1, 0x54, 0x05, 0xd9, 0x01, 0xeb, 0xcb, 0x72, 0x0f, 0x63, 0x41, 0xb1, 0x3e, 0x05, 0x8a, + 0x8b, 0xd3, 0xa0, 0xb8, 0x54, 0x09, 0x8a, 0xcb, 0xf9, 0xf8, 0x17, 0x62, 0xbc, 0x52, 0x8e, 0xb1, + 0x05, 0xab, 0x46, 0x06, 0x56, 0x29, 0x26, 0x34, 0x33, 0x4c, 0x28, 0x42, 0x2b, 0xdc, 0x06, 0xad, + 0xad, 0x12, 0xb4, 0x56, 0x41, 0x43, 0xbb, 0x12, 0x1a, 0x34, 0x24, 0x4a, 0x22, 0x27, 0x42, 0x07, + 0x67, 0xc9, 0x35, 0x94, 0x4a, 0x27, 0x25, 0x7f, 0x22, 0xa8, 0xef, 0xac, 0x25, 0xe9, 0x34, 0x22, + 0xe2, 0x1b, 0x41, 0x7d, 0x74, 0x0f, 0x56, 0x73, 0x67, 0x5f, 0xc4, 0x9d, 0x75, 0x3d, 0xde, 0xce, + 0x4e, 0xbf, 0x88, 0xa3, 0x1f, 0xc0, 0x9a, 0x9d, 0x64, 0x0e, 0xd0, 0x8e, 0x9e, 0x65, 0x97, 0xba, + 0x9a, 0x89, 0xbf, 0x80, 0x3b, 0xaf, 0xe8, 0x5b, 0x73, 0x9c, 0xdb, 0x3a, 0x3e, 0x00, 0x88, 0x89, + 0x10, 0xf1, 0x98, 0xab, 0x02, 0xaa, 0xd9, 0x62, 0xb4, 0x1c, 0x7c, 0x02, 0x28, 0xbf, 0x28, 0x3b, + 0xfe, 0xab, 0x7b, 0x09, 0x1c, 0xc2, 0xe6, 0x37, 0x4c, 0x61, 0x40, 0x49, 0xcf, 0xec, 0xee, 0xa3, + 0x68, 0xc1, 0x42, 0xd9, 0x02, 0x55, 0xe0, 0xfe, 0x84, 0x93, 0xf4, 0x3c, 0x58, 0x74, 0x53, 0x1a, + 0xf7, 0x60, 0xab, 0xa4, 0xad, 0xb2, 0x97, 0x68, 0xd8, 0x5e, 0x42, 0x6d, 0xe7, 0xe5, 0x07, 0x18, + 0x87, 0x3f, 0x83, 0x8d, 0x97, 0x1f, 0x20, 0xfe, 0x17, 0xb0, 0x7e, 0x11, 0x8c, 0x58, 0x1e, 0x28, + 0x67, 0x6f, 0xdc, 0xd6, 0xcd, 0x42, 0x92, 0x87, 0xba, 0x6e, 0x3a, 0x50, 0x27, 0xe1, 0xc8, 0xb4, + 0x49, 0xea, 0x13, 0x7f, 0x02, 0x9d, 0x4c, 0x64, 0x56, 0x71, 0x53, 0xa7, 0x1a, 0x83, 0xdd, 0x33, + 0xca, 0x28, 0x57, 0x18, 0x45, 0x98, 0x1f, 0x5d, 0x5e, 0x50, 0xea, 0xcf, 0x37, 0xa2, 0x02, 0x8d, + 0xdb, 0xdf, 0x07, 0x8d, 0xf1, 0x6b, 0xe8, 0x56, 0xe9, 0xcb, 0x5a, 0xef, 0x2b, 0x3e, 0xec, 0x0b, + 0x4a, 0x7d, 0x63, 0xe5, 0xca, 0x15, 0x1f, 0xaa, 0x29, 0xaa, 0x9a, 0xd4, 0x50, 0xcc, 0xa3, 0x68, + 0x68, 0xf4, 0xa9, 0xb9, 0xe7, 0x8a, 0xc6, 0x7f, 0x80, 0x43, 0xb5, 0xdb, 0x1c, 0xcc, 0x9c, 0xa7, + 0x99, 0x60, 0x37, 0xf3, 0x13, 0x68, 0xe5, 0xcf, 0xb0, 0x9a, 0x86, 0xcf, 0xdd, 0x2a, 0x18, 0x4b, + 0x5a, 0x9a, 0xfc, 0xec, 0x79, 0xd9, 0x86, 0x7f, 0x04, 0x47, 0xb7, 0x18, 0x70, 0x8b, 0xff, 0x95, + 0xe5, 0xc5, 0xae, 0xe2, 0xff, 0x6c, 0x79, 0x0f, 0x3a, 0x67, 0x06, 0xb1, 0x52, 0x43, 0x0b, 0xb0, + 0x56, 0x2b, 0xc2, 0x1a, 0x3e, 0x82, 0xd6, 0xbc, 0x13, 0xfd, 0x21, 0xb4, 0xce, 0x48, 0x76, 0xf5, + 0xe8, 0x40, 0x5d, 0xf5, 0xd7, 0xc9, 0x0c, 0xf5, 0xa9, 0x38, 0x59, 0x4f, 0xae, 0x3e, 0xf1, 0x23, + 0x58, 0x7b, 0x9e, 0x9c, 0x76, 0x76, 0xd5, 0xc7, 0xb0, 0x9c, 0x9c, 0x7f, 0xba, 0x6b, 0x6e, 0x9d, + 0xb6, 0xcd, 0x86, 0xf5, 0x34, 0xd7, 0x8c, 0xe1, 0x87, 0xb0, 0xa4, 0x19, 0x1f, 0x70, 0xc5, 0xfe, + 0x04, 0xda, 0xe7, 0x31, 0x8f, 0x86, 0xb9, 0xf6, 0x27, 0x0c, 0x84, 0xa4, 0xcc, 0x76, 0x6f, 0x09, + 0x85, 0xef, 0xc3, 0xaa, 0x99, 0x37, 0xa7, 0x7c, 0xbf, 0x84, 0x3b, 0x67, 0x54, 0x3e, 0xd3, 0x2f, + 0x06, 0xe9, 0xe4, 0x63, 0x58, 0x4e, 0xde, 0x10, 0x4c, 0xbc, 0x3a, 0x27, 0xc9, 0xe3, 0x42, 0x72, + 0x4a, 0xab, 0x99, 0x66, 0xfc, 0xf4, 0xef, 0x00, 0xf0, 0x24, 0x0e, 0x2e, 0x28, 0xbf, 0x52, 0xc7, + 0xc6, 0x1b, 0x68, 0xe5, 0x6e, 0xa5, 0x68, 0xc7, 0x6c, 0xbb, 0xfc, 0x2a, 0xd0, 0xb5, 0x27, 0x70, + 0xc5, 0x15, 0x16, 0xef, 0xbe, 0xff, 0xc7, 0xbf, 0xff, 0xb4, 0xb0, 0x81, 0xee, 0xf4, 0xae, 0x1e, + 0xf6, 0x26, 0x82, 0xf2, 0x1e, 0xa3, 0x03, 0xdd, 0x88, 0xa0, 0xdf, 0xc2, 0xce, 0x4b, 0x22, 0xa9, + 0x90, 0x2f, 0x38, 0xa7, 0xfa, 0xc2, 0x38, 0x08, 0xa9, 0x6e, 0xbf, 0x66, 0xab, 0xda, 0x34, 0x03, + 0x85, 0x2e, 0x0d, 0x6f, 0x6a, 0x25, 0x6b, 0xa8, 0x9d, 0x2a, 0x51, 0x97, 0x5f, 0x0e, 0xeb, 0xa5, + 0xdb, 0x1f, 0xda, 0xcf, 0x2c, 0xad, 0xb8, 0x61, 0x76, 0x0f, 0x66, 0x0d, 0x1b, 0x3d, 0x87, 0x5a, + 0x4f, 0x17, 0x6f, 0xa5, 0x7a, 0x88, 0xb9, 0xdc, 0xaa, 0x69, 0x8f, 0x6b, 0x0f, 0xd0, 0x39, 0x2c, + 0xaa, 0x2b, 0x21, 0x9a, 0x5d, 0x13, 0xdd, 0x0d, 0x7b, 0x71, 0xc9, 0x5d, 0x1d, 0xb1, 0xa3, 0x25, + 0x23, 0xbc, 0x9a, 0x4a, 0xf6, 0x48, 0x18, 0x2a, 0x89, 0x37, 0x80, 0xa6, 0x6f, 0x07, 0xe8, 0xd0, + 0x08, 0x99, 0x79, 0x71, 0x48, 0xf7, 0x32, 0xe3, 0xa6, 0x80, 0xb1, 0xd6, 0xb8, 0x87, 0x77, 0x52, + 0x8d, 0x9c, 0xbc, 0xcd, 0x95, 0xab, 0xd2, 0x3d, 0x86, 0xb5, 0xe2, 0x55, 0x00, 0xed, 0x65, 0x1e, + 0x9a, 0xbe, 0x21, 0xcc, 0x88, 0xce, 0xb4, 0xa6, 0x51, 0x61, 0xb5, 0xd2, 0xc4, 0xa0, 0x53, 0xbe, + 0x13, 0xa0, 0x83, 0x69, 0x5d, 0xf9, 0xcb, 0xc2, 0x0c, 0x6d, 0x1f, 0x6b, 0x6d, 0x07, 0x78, 0xb7, + 0x4a, 0x9b, 0x5e, 0xaf, 0xf4, 0xbd, 0xaf, 0xe9, 0x5b, 0x4e, 0xc1, 0x31, 0x1e, 0x0d, 0x62, 0x89, + 0x70, 0xa6, 0x75, 0xd6, 0xdd, 0xa1, 0x7b, 0x4b, 0xcb, 0x89, 0x3f, 0xd5, 0xfa, 0xef, 0xe1, 0x83, + 0xbc, 0xfe, 0x69, 0x3d, 0xca, 0x88, 0x3e, 0x34, 0xd3, 0x07, 0xba, 0x34, 0xe5, 0xcb, 0xcf, 0x7b, + 0x5d, 0x67, 0x7a, 0xc0, 0xa8, 0xda, 0xd7, 0xaa, 0x76, 0x30, 0x4a, 0x55, 0x09, 0x3b, 0xe7, 0x71, + 0xed, 0xc1, 0xe7, 0x35, 0x53, 0xc0, 0x16, 0x54, 0x67, 0x57, 0x95, 0x1d, 0x28, 0xc3, 0x2f, 0xde, + 0xd3, 0x1a, 0xb6, 0xd1, 0x66, 0x7e, 0x33, 0xa9, 0xbc, 0x37, 0xd0, 0x7a, 0x9e, 0x3d, 0x51, 0xdc, + 0x96, 0xf3, 0x28, 0x53, 0x90, 0xca, 0xfe, 0x48, 0xcb, 0xde, 0xc5, 0x99, 0xec, 0xdc, 0x7b, 0x87, + 0x72, 0x0f, 0xd1, 0xf5, 0x9b, 0x60, 0xb1, 0x49, 0x3f, 0x2b, 0x27, 0x1f, 0x8c, 0xad, 0x3c, 0x1a, + 0x67, 0xe2, 0xef, 0x69, 0xf1, 0xfb, 0xd8, 0xc9, 0x9b, 0x9e, 0x17, 0x96, 0xa8, 0x80, 0xec, 0x95, + 0x04, 0xdd, 0xb5, 0x09, 0x55, 0xf1, 0xd0, 0xd2, 0xdd, 0xcd, 0xf2, 0xa2, 0xf4, 0xaa, 0x82, 0xef, + 0x6a, 0x55, 0x5b, 0xb8, 0x93, 0xaa, 0xf2, 0x93, 0x19, 0x8f, 0x6b, 0x0f, 0x4e, 0xff, 0x0a, 0xd0, + 0x7e, 0xe2, 0x5f, 0x06, 0xcc, 0xa2, 0xea, 0xb7, 0xd0, 0xb0, 0x4f, 0x62, 0xf3, 0x23, 0x52, 0x7e, + 0x3c, 0xc3, 0x5d, 0xad, 0x6b, 0x13, 0xe9, 0x98, 0x13, 0x25, 0x37, 0xc5, 0x20, 0xe4, 0x01, 0x64, + 0xad, 0x2e, 0xb2, 0x79, 0x33, 0xd5, 0x32, 0xa7, 0x5b, 0x99, 0xee, 0x8b, 0x8b, 0x08, 0x57, 0x10, + 0xdf, 0x63, 0xf4, 0xad, 0x72, 0x59, 0x04, 0xab, 0x85, 0x8e, 0x35, 0xf5, 0x5a, 0x55, 0xd7, 0xdc, + 0xdd, 0xab, 0x1e, 0xac, 0x8a, 0x51, 0x51, 0xdb, 0x44, 0x2f, 0x50, 0x0a, 0x47, 0xd0, 0xca, 0x75, + 0xb0, 0x69, 0x96, 0x4d, 0x77, 0xc1, 0x69, 0x59, 0x56, 0x34, 0xbc, 0xf8, 0x48, 0xab, 0xba, 0x8b, + 0xb7, 0xa7, 0x55, 0x59, 0x45, 0x0c, 0xd6, 0x4b, 0x60, 0x79, 0x5b, 0x4a, 0xcf, 0xc3, 0xd7, 0x0a, + 0x4f, 0x96, 0xd0, 0xf5, 0xd7, 0xd0, 0xb0, 0x8d, 0x31, 0xb2, 0xaf, 0x59, 0xa5, 0xe6, 0x3b, 0xcd, + 0x83, 0x72, 0x07, 0x8d, 0x0f, 0xb4, 0x78, 0x07, 0x6f, 0x64, 0xe2, 0x45, 0x30, 0x62, 0xbd, 0xb1, + 0xc9, 0xec, 0xf7, 0x35, 0x40, 0xd3, 0xed, 0x6d, 0x7a, 0x6e, 0xcc, 0xec, 0xb4, 0xbb, 0x47, 0xb7, + 0xcc, 0x30, 0xba, 0xef, 0x6b, 0xdd, 0x47, 0x78, 0x2f, 0xd3, 0x3d, 0x9a, 0x9a, 0xad, 0x8c, 0xf8, + 0x63, 0x0d, 0xf6, 0x4b, 0xcd, 0xe8, 0xaf, 0x02, 0x39, 0xce, 0xfa, 0x4a, 0x74, 0x3f, 0xb7, 0xbf, + 0xdb, 0x3a, 0xcf, 0xee, 0xf1, 0xfc, 0x89, 0xc5, 0x8e, 0x03, 0xaf, 0x15, 0x3d, 0xa3, 0xec, 0xf9, + 0xb3, 0xb2, 0xa7, 0x18, 0xaf, 0x59, 0xf6, 0xcc, 0xe9, 0x84, 0xe7, 0x86, 0xff, 0x44, 0x5b, 0x71, + 0x8c, 0xef, 0x55, 0x86, 0xbf, 0xa8, 0x55, 0x99, 0x76, 0x01, 0x70, 0x21, 0x09, 0x97, 0xba, 0xcf, + 0x43, 0xb6, 0x47, 0xc8, 0x77, 0x87, 0xe9, 0x79, 0x57, 0x68, 0x05, 0x2d, 0x20, 0xe0, 0xf5, 0x4c, + 0x51, 0xac, 0x26, 0x24, 0x19, 0xd6, 0x4c, 0xdb, 0xc1, 0xd9, 0x58, 0xe3, 0x64, 0xc8, 0x56, 0xec, + 0x1c, 0x2d, 0xb0, 0xa1, 0x8d, 0x7c, 0xa0, 0xad, 0xbc, 0x6f, 0xa1, 0x61, 0x7f, 0x05, 0xcd, 0xc7, + 0xb1, 0xf2, 0x4f, 0xa3, 0x2a, 0x1c, 0x63, 0x91, 0x4f, 0x03, 0x36, 0x8c, 0x06, 0xcb, 0xfa, 0x1f, + 0xc4, 0x17, 0xff, 0x0d, 0x00, 0x00, 0xff, 0xff, 0x42, 0xe3, 0x2b, 0x72, 0x8f, 0x1b, 0x00, 0x00, } diff --git a/rpc/pb/rpc.pb.gw.go b/rpc/pb/rpc.pb.gw.go index 882c34d70..25755278a 100644 --- a/rpc/pb/rpc.pb.gw.go +++ b/rpc/pb/rpc.pb.gw.go @@ -50,7 +50,7 @@ func request_ApiService_GetAccountState_0(ctx context.Context, marshaler runtime var protoReq GetAccountStateRequest var metadata runtime.ServerMetadata - if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil { + if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } @@ -63,7 +63,7 @@ func request_ApiService_Call_0(ctx context.Context, marshaler runtime.Marshaler, var protoReq TransactionRequest var metadata runtime.ServerMetadata - if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil { + if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } @@ -76,7 +76,7 @@ func request_ApiService_SendRawTransaction_0(ctx context.Context, marshaler runt var protoReq SendRawTransactionRequest var metadata runtime.ServerMetadata - if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil { + if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } @@ -89,7 +89,7 @@ func request_ApiService_GetBlockByHash_0(ctx context.Context, marshaler runtime. var protoReq GetBlockByHashRequest var metadata runtime.ServerMetadata - if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil { + if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } @@ -102,7 +102,7 @@ func request_ApiService_GetBlockByHeight_0(ctx context.Context, marshaler runtim var protoReq GetBlockByHeightRequest var metadata runtime.ServerMetadata - if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil { + if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } @@ -115,7 +115,7 @@ func request_ApiService_GetTransactionReceipt_0(ctx context.Context, marshaler r var protoReq GetTransactionByHashRequest var metadata runtime.ServerMetadata - if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil { + if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } @@ -128,7 +128,7 @@ func request_ApiService_Subscribe_0(ctx context.Context, marshaler runtime.Marsh var protoReq SubscribeRequest var metadata runtime.ServerMetadata - if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil { + if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } @@ -158,7 +158,7 @@ func request_ApiService_EstimateGas_0(ctx context.Context, marshaler runtime.Mar var protoReq TransactionRequest var metadata runtime.ServerMetadata - if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil { + if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } @@ -171,7 +171,7 @@ func request_ApiService_GetEventsByHash_0(ctx context.Context, marshaler runtime var protoReq HashRequest var metadata runtime.ServerMetadata - if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil { + if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } @@ -184,7 +184,7 @@ func request_ApiService_GetDynasty_0(ctx context.Context, marshaler runtime.Mars var protoReq ByBlockHeightRequest var metadata runtime.ServerMetadata - if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil { + if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } @@ -206,7 +206,7 @@ func request_AdminService_NewAccount_0(ctx context.Context, marshaler runtime.Ma var protoReq NewAccountRequest var metadata runtime.ServerMetadata - if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil { + if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } @@ -219,7 +219,7 @@ func request_AdminService_UnlockAccount_0(ctx context.Context, marshaler runtime var protoReq UnlockAccountRequest var metadata runtime.ServerMetadata - if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil { + if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } @@ -232,7 +232,7 @@ func request_AdminService_LockAccount_0(ctx context.Context, marshaler runtime.M var protoReq LockAccountRequest var metadata runtime.ServerMetadata - if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil { + if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } @@ -245,7 +245,7 @@ func request_AdminService_SendTransaction_0(ctx context.Context, marshaler runti var protoReq TransactionRequest var metadata runtime.ServerMetadata - if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil { + if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } @@ -258,7 +258,7 @@ func request_AdminService_SignHash_0(ctx context.Context, marshaler runtime.Mars var protoReq SignHashRequest var metadata runtime.ServerMetadata - if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil { + if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } @@ -267,11 +267,24 @@ func request_AdminService_SignHash_0(ctx context.Context, marshaler runtime.Mars } +func request_AdminService_GenerateRandomSeed_0(ctx context.Context, marshaler runtime.Marshaler, client AdminServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq GenerateRandomSeedRequest + var metadata runtime.ServerMetadata + + if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.GenerateRandomSeed(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + func request_AdminService_SignTransactionWithPassphrase_0(ctx context.Context, marshaler runtime.Marshaler, client AdminServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq SignTransactionPassphraseRequest var metadata runtime.ServerMetadata - if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil { + if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } @@ -284,7 +297,7 @@ func request_AdminService_SendTransactionWithPassphrase_0(ctx context.Context, m var protoReq SendTransactionPassphraseRequest var metadata runtime.ServerMetadata - if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil { + if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } @@ -297,7 +310,7 @@ func request_AdminService_StartPprof_0(ctx context.Context, marshaler runtime.Ma var protoReq PprofRequest var metadata runtime.ServerMetadata - if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil { + if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } @@ -1010,6 +1023,35 @@ func RegisterAdminServiceHandlerClient(ctx context.Context, mux *runtime.ServeMu }) + mux.Handle("POST", pattern_AdminService_GenerateRandomSeed_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + if cn, ok := w.(http.CloseNotifier); ok { + go func(done <-chan struct{}, closed <-chan bool) { + select { + case <-done: + case <-closed: + cancel() + } + }(ctx.Done(), cn.CloseNotify()) + } + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_AdminService_GenerateRandomSeed_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_AdminService_GenerateRandomSeed_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + mux.Handle("POST", pattern_AdminService_SignTransactionWithPassphrase_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() @@ -1171,6 +1213,8 @@ var ( pattern_AdminService_SignHash_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"v1", "admin", "sign", "hash"}, "")) + pattern_AdminService_GenerateRandomSeed_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "admin", "generateRandomSeed"}, "")) + pattern_AdminService_SignTransactionWithPassphrase_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "admin", "sign"}, "")) pattern_AdminService_SendTransactionWithPassphrase_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "admin", "transactionWithPassphrase"}, "")) @@ -1195,6 +1239,8 @@ var ( forward_AdminService_SignHash_0 = runtime.ForwardResponseMessage + forward_AdminService_GenerateRandomSeed_0 = runtime.ForwardResponseMessage + forward_AdminService_SignTransactionWithPassphrase_0 = runtime.ForwardResponseMessage forward_AdminService_SendTransactionWithPassphrase_0 = runtime.ForwardResponseMessage diff --git a/rpc/pb/rpc.proto b/rpc/pb/rpc.proto index 08d54d7cb..b04d78f24 100644 --- a/rpc/pb/rpc.proto +++ b/rpc/pb/rpc.proto @@ -174,6 +174,13 @@ service AdminService { }; } + rpc GenerateRandomSeed(GenerateRandomSeedRequest) returns (GenerateRandomSeedResponse) { + option (google.api.http) = { + post: "/v1/admin/generateRandomSeed" + body: "*" + }; + } + // Sign sign transaction rpc SignTransactionWithPassphrase(SignTransactionPassphraseRequest) returns (SignTransactionPassphraseResponse) { option (google.api.http) = { @@ -359,6 +366,9 @@ message TransactionRequest { // binary data for transaction bytes binary = 10; + + // transaction payload type, enum:binary, deploy, call + string type = 20; } message ContractRequest { @@ -497,6 +507,12 @@ message TransactionResponse { // transaction gas used string gas_used = 14; + + // contract execute error + string execute_error = 15; + + // contract execute result + string execute_result = 16; } message NewAccountRequest { @@ -541,6 +557,20 @@ message SignHashResponse { bytes data = 1; } +message GenerateRandomSeedRequest { + // miner address + string address = 1; + // parent hash of new block + bytes parent_hash = 2; + // height of new block + uint64 height = 3; +} + +message GenerateRandomSeedResponse { + bytes vrf_seed = 1; + bytes vrf_proof = 2; +} + message SignTransactionPassphraseRequest { // transaction struct TransactionRequest transaction = 1; diff --git a/storage/metrics.go b/storage/metrics.go index 2187ef7f9..d7761ee7e 100644 --- a/storage/metrics.go +++ b/storage/metrics.go @@ -27,4 +27,9 @@ var ( // rocksdb metrics metricsRocksdbFlushTime = metrics.NewGauge("neb.rocksdb.flushtime") metricsRocksdbFlushLen = metrics.NewGauge("neb.rocksdb.flushlen") + + metricsBlocksdbCacheSize = metrics.NewGauge("neb.rocksdb.cache.size") //block_cache->GetUsage() + metricsBlocksdbCachePinnedSize = metrics.NewGauge("neb.rocksdb.cachepinned.size") //block_cache->GetPinnedUsage() + metricsBlocksdbTableReaderMem = metrics.NewGauge("neb.rocksdb.tablereader.mem") //estimate-table-readers-mem + metricsBlocksdbAllMemTables = metrics.NewGauge("neb.rocksdb.alltables.mem") //cur-size-all-mem-tables ) diff --git a/storage/rocks_storage.go b/storage/rocks_storage.go index 7d7105fed..92ec584c4 100644 --- a/storage/rocks_storage.go +++ b/storage/rocks_storage.go @@ -1,6 +1,7 @@ package storage import ( + "strconv" "sync" "time" @@ -18,6 +19,8 @@ type RocksStorage struct { ro *gorocksdb.ReadOptions wo *gorocksdb.WriteOptions + + cache *gorocksdb.Cache } // NewRocksStorage init a storage @@ -26,7 +29,9 @@ func NewRocksStorage(path string) (*RocksStorage, error) { filter := gorocksdb.NewBloomFilter(10) bbto := gorocksdb.NewDefaultBlockBasedTableOptions() bbto.SetFilterPolicy(filter) - bbto.SetBlockCache(gorocksdb.NewLRUCache(512 << 20)) + + cache := gorocksdb.NewLRUCache(512 << 20) + bbto.SetBlockCache(cache) opts := gorocksdb.NewDefaultOptions() opts.SetBlockBasedTableFactory(bbto) opts.SetCreateIfMissing(true) @@ -39,13 +44,18 @@ func NewRocksStorage(path string) (*RocksStorage, error) { return nil, err } - return &RocksStorage{ + storage := &RocksStorage{ db: db, + cache: cache, enableBatch: false, batchOpts: make(map[string]*batchOpt), ro: gorocksdb.NewDefaultReadOptions(), wo: gorocksdb.NewDefaultWriteOptions(), - }, nil + } + + //go RecordMetrics(storage) + + return storage, nil } // Get return value to the key in Storage @@ -151,3 +161,33 @@ func (storage *RocksStorage) DisableBatch() { storage.enableBatch = false } + +// RecordMetrics record rocksdb metrics +func RecordMetrics(storage *RocksStorage) { + metricsUpdateChan := time.NewTicker(5 * time.Second).C + + for { + select { + case <-metricsUpdateChan: + + readersMemStr := storage.db.GetProperty("rocksdb.estimate-table-readers-mem") + allMemTablesStr := storage.db.GetProperty("rocksdb.cur-size-all-mem-tables") + cacheSize := storage.cache.GetUsage() + pinnedSize := storage.cache.GetPinnedUsage() + + readersMem, err := strconv.Atoi(readersMemStr) + if err != nil { + break + } + allMemTables, err := strconv.Atoi(allMemTablesStr) + if err != nil { + break + } + + metricsBlocksdbAllMemTables.Update(int64(allMemTables)) + metricsBlocksdbTableReaderMem.Update(int64(readersMem)) + metricsBlocksdbCacheSize.Update(int64(cacheSize)) + metricsBlocksdbCachePinnedSize.Update(int64(pinnedSize)) + } + } +} diff --git a/storage/rocks_storage_test.go b/storage/rocks_storage_test.go index ecbf89c21..20070222f 100644 --- a/storage/rocks_storage_test.go +++ b/storage/rocks_storage_test.go @@ -17,7 +17,7 @@ func TestNewRocksStorage(t *testing.T) { want *RocksStorage wantErr bool }{ - // TODO: Add test cases. + // TODO: Add test cases. } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) {