Commit 5427ab5b authored by vipwzw's avatar vipwzw

add query

parent cbcc2979
......@@ -5,23 +5,31 @@ import "errors"
//ErrInvalidFuncFormat 错误的函数调用格式(没有_)
var errInvalidFuncFormat = errors.New("chain33.js: invalid function name format")
//ErrInvalidFuncPrefix not exec_ execloal execdellocal
//ErrInvalidFuncPrefix not exec_ execloal_ query_
var errInvalidFuncPrefix = errors.New("chain33.js: invalid function prefix format")
//ErrFuncNotFound 函数没有找到
var errFuncNotFound = errors.New("chain33.js: invalid function name not found")
var callcode = `
var tojson = JSON.stringify
function kvcreator(dbtype) {
this.data = {}
this.kvs = []
this.logs = []
this.logs = []
this.type = dbtype
this.getstate = getstatedb
this.getloal = getlocaldb
this.list = listdb
if (dbtype == "exec" || dbtype == "init") {
this.get = getstatedb
this.get = getstatedb
} else if (dbtype == "local") {
this.get = getlocaldb
this.list = listdb
}
} else if (dbtype == "query") {
this.get = getlocaldb
} else {
throw new Error("chain33.js: dbtype error")
}
}
kvcreator.prototype.add = function(k, v) {
......@@ -37,7 +45,11 @@ kvcreator.prototype.get = function(k) {
if (this.data[k]) {
v = this.data[k]
} else {
v = this.get(k)
var dbvalue = this.get(k)
if (dbvalue.err != "") {
return null
}
v = dbvalue.value
}
if (!v) {
return null
......@@ -46,7 +58,11 @@ kvcreator.prototype.get = function(k) {
}
kvcreator.prototype.listvalue = function(prefix, key, count, direction) {
var values = this.list(prefix, key, count, direction)
var dbvalues = this.list(prefix, key, count, direction)
if (dbvalues.err != "") {
return []
}
var values = dbvalues.value
if (!values || values.length == 0) {
return []
}
......@@ -58,8 +74,8 @@ kvcreator.prototype.listvalue = function(prefix, key, count, direction) {
}
kvcreator.prototype.addlog = function(log) {
if (this.list) {
throw new Error("local or dellocal can't set log")
if (this.type != "exec") {
throw new Error("local or query can't set log")
}
if (typeof v != "string") {
log = JSON.stringify(log)
......@@ -93,7 +109,9 @@ function callcode(context, f, args, loglist) {
runobj = new Exec(JSON.parse(context))
} else if (prefix == "execlocal") {
runobj = new ExecLocal(JSON.parse(context), logs)
} else {
} else if (prefix == "query") {
runobj = new Query(JSON.parse(context))
} else {
throw new Error("chain33.js: invalid function prefix format")
}
var arg = JSON.parse(args)
......
......@@ -9,7 +9,8 @@ import (
func (c *js) Exec_Create(payload *jsproto.Create, tx *types.Transaction, index int) (*types.Receipt, error) {
execer := types.ExecName("user.js." + payload.Name)
kvc := dapp.NewKVCreator(c.GetStateDB(), calcStatePrefix([]byte(execer)), nil)
c.prefix = calcStatePrefix([]byte(execer))
kvc := dapp.NewKVCreator(c.GetStateDB(), c.prefix, nil)
_, err := kvc.GetNoPrefix(calcCodeKey(payload.Name))
if err != nil && err != types.ErrNotFound {
return nil, err
......@@ -33,7 +34,8 @@ func (c *js) Exec_Create(payload *jsproto.Create, tx *types.Transaction, index i
func (c *js) Exec_Call(payload *jsproto.Call, tx *types.Transaction, index int) (*types.Receipt, error) {
execer := types.ExecName("user.js." + payload.Name)
kvc := dapp.NewKVCreator(c.GetStateDB(), calcStatePrefix([]byte(execer)), nil)
c.prefix = calcStatePrefix([]byte(execer))
kvc := dapp.NewKVCreator(c.GetStateDB(), c.prefix, nil)
jsvalue, err := c.callVM("exec", payload, tx, index, nil)
if err != nil {
return nil, err
......
......@@ -13,7 +13,8 @@ func (c *js) ExecDelLocal_Create(payload *jsproto.Create, tx *types.Transaction,
func (c *js) ExecDelLocal_Call(payload *jsproto.Call, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
krollback := calcRollbackKey(tx.Hash())
execer := types.ExecName("user.js." + payload.Name)
kvc := dapp.NewKVCreator(c.GetLocalDB(), calcLocalPrefix([]byte(execer)), krollback)
c.prefix = calcLocalPrefix([]byte(execer))
kvc := dapp.NewKVCreator(c.GetLocalDB(), c.prefix, krollback)
kvs, err := kvc.GetRollbackKVList()
if err != nil {
return nil, err
......
......@@ -13,7 +13,8 @@ func (c *js) ExecLocal_Create(payload *jsproto.Create, tx *types.Transaction, re
func (c *js) ExecLocal_Call(payload *jsproto.Call, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
k := calcRollbackKey(tx.Hash())
execer := types.ExecName("user.js." + payload.Name)
kvc := dapp.NewKVCreator(c.GetLocalDB(), calcLocalPrefix([]byte(execer)), k)
c.prefix = calcLocalPrefix([]byte(execer))
kvc := dapp.NewKVCreator(c.GetLocalDB(), c.prefix, k)
jsvalue, err := c.callVM("execlocal", payload, tx, index, receiptData)
if err != nil {
return nil, err
......
......@@ -30,6 +30,8 @@ func Init(name string, sub []byte) {
type js struct {
drivers.DriverBase
prefix []byte
localprefix []byte
}
func newjs() drivers.Driver {
......@@ -51,7 +53,7 @@ func (u *js) GetDriverName() string {
func (u *js) callVM(prefix string, payload *jsproto.Call, tx *types.Transaction,
index int, receiptData *types.ReceiptData) (*otto.Object, error) {
vm, err := u.createVM(tx, index)
vm, err := u.createVM(payload.Name, tx, index)
if err != nil {
return nil, err
}
......@@ -77,6 +79,13 @@ func (u *js) callVM(prefix string, payload *jsproto.Call, tx *types.Transaction,
if err != nil {
return nil, err
}
if prefix == "query" {
s, err := jsvalue.ToString()
if err != nil {
return nil, err
}
return newObject(vm).setValue("result", s).object(), nil
}
if !jsvalue.IsObject() {
return nil, ptypes.ErrJsReturnNotObject
}
......@@ -104,6 +113,10 @@ func jslogs(receiptData *types.ReceiptData) ([]string, error) {
}
func (u *js) getContext(tx *types.Transaction, index int64) *blockContext {
var hash [32]byte
if tx != nil {
copy(hash[:], tx.Hash())
}
return &blockContext{
Height: u.GetHeight(),
Name: u.GetName(),
......@@ -111,41 +124,44 @@ func (u *js) getContext(tx *types.Transaction, index int64) *blockContext {
Curname: u.GetCurrentExecName(),
DriverName: u.GetDriverName(),
Difficulty: u.GetDifficulty(),
TxHash: common.ToHex(tx.Hash()),
TxHash: common.ToHex(hash[:]),
Index: index,
}
}
func (u *js) createVM(tx *types.Transaction, index int) (*otto.Otto, error) {
data, err := json.Marshal(u.getContext(tx, int64(index)))
if err != nil {
return nil, err
}
vm := otto.New()
vm.Set("context", string(data))
func (u *js) getstatedbFunc(vm *otto.Otto, name string) {
prefix, _ := calcAllPrefix(name)
vm.Set("getstatedb", func(call otto.FunctionCall) otto.Value {
key, err := call.Argument(0).ToString()
if err != nil {
return errReturn(vm, err)
}
v, err := u.getstatedb(key)
v, err := u.getstatedb(string(prefix) + key)
if err != nil {
return errReturn(vm, err)
}
return okReturn(vm, v)
})
}
func (u *js) getlocaldbFunc(vm *otto.Otto, name string) {
_, prefix := calcAllPrefix(name)
vm.Set("getlocaldb", func(call otto.FunctionCall) otto.Value {
key, err := call.Argument(0).ToString()
if err != nil {
return errReturn(vm, err)
}
v, err := u.getlocaldb(key)
v, err := u.getlocaldb(string(prefix) + key)
if err != nil {
return errReturn(vm, err)
}
return okReturn(vm, v)
})
}
func (u *js) listdbFunc(vm *otto.Otto, name string) {
//List(prefix, key []byte, count, direction int32) ([][]byte, error)
_, plocal := calcAllPrefix(name)
vm.Set("listdb", func(call otto.FunctionCall) otto.Value {
prefix, err := call.Argument(0).ToString()
if err != nil {
......@@ -163,27 +179,73 @@ func (u *js) createVM(tx *types.Transaction, index int) (*otto.Otto, error) {
if err != nil {
return errReturn(vm, err)
}
v, err := u.listdb(prefix, key, int32(count), int32(direction))
v, err := u.listdb(string(plocal)+prefix, key, int32(count), int32(direction))
if err != nil {
return errReturn(vm, err)
}
return listReturn(vm, v)
})
}
func (u *js) createVM(name string, tx *types.Transaction, index int) (*otto.Otto, error) {
data, err := json.Marshal(u.getContext(tx, int64(index)))
if err != nil {
return nil, err
}
vm := otto.New()
vm.Set("context", string(data))
u.getstatedbFunc(vm, name)
u.getlocaldbFunc(vm, name)
u.listdbFunc(vm, name)
return vm, nil
}
func errReturn(vm *otto.Otto, err error) otto.Value {
v, _ := vm.ToValue(&dbReturn{Err: err.Error()})
return v
return newObject(vm).setErr(err).value()
}
func okReturn(vm *otto.Otto, value string) otto.Value {
v, _ := vm.ToValue(&dbReturn{Value: value})
return v
return newObject(vm).setValue("value", value).value()
}
func listReturn(vm *otto.Otto, value []string) otto.Value {
v, _ := vm.ToValue(&listdbReturn{Value: value})
return newObject(vm).setValue("value", value).value()
}
type object struct {
vm *otto.Otto
obj *otto.Object
}
func newObject(vm *otto.Otto) *object {
obj, err := vm.Object("({})")
if err != nil {
panic(err)
}
return &object{vm: vm, obj: obj}
}
func (o *object) setErr(err error) *object {
if err != nil {
o.obj.Set("err", err.Error())
}
return o
}
func (o *object) setValue(key string, value interface{}) *object {
o.obj.Set(key, value)
return o
}
func (o *object) object() *otto.Object {
return o.obj
}
func (o *object) value() otto.Value {
v, err := otto.ToValue(o.obj)
if err != nil {
panic(err)
}
return v
}
......
......@@ -21,16 +21,6 @@ type blockContext struct {
Index int64 `json:"index"`
}
type dbReturn struct {
Value string `json:"value"`
Err string `json:"err"`
}
type listdbReturn struct {
Value []string `json:"value"`
Err string `json:"err"`
}
func parseJsReturn(jsvalue *otto.Object) (kvlist []*types.KeyValue, logs []*types.ReceiptLog, err error) {
//kvs
obj, err := getObject(jsvalue, "kvs")
......
......@@ -37,6 +37,11 @@ function ExecLocal(context, logs) {
this.logs = logs
}
function Query(context) {
this.kvc = new kvcreator("query")
this.context = context
}
Exec.prototype.hello = function(args) {
this.kvc.add("args", args)
this.kvc.add("action", "exec")
......@@ -53,6 +58,12 @@ ExecLocal.prototype.hello = function(args) {
this.kvc.add("context", this.context)
return this.kvc.receipt()
}
//return a json string
Query.prototype.hello = function(args) {
var obj = getlocaldb("context")
return tojson(obj)
}
`
func initExec(ldb db.DB, kvdb db.KVDB, t *testing.T) *js {
......@@ -119,6 +130,15 @@ func TestCallcode(t *testing.T) {
assert.Equal(t, int64(1), data.Height)
assert.Equal(t, int64(0), data.Index)
//call query
jsondata, err := e.Query_Query(call)
assert.Nil(t, err)
err = json.Unmarshal([]byte(jsondata.(*jsproto.QueryResult).Data), &data)
assert.Nil(t, err)
assert.Equal(t, uint64(1), data.Difficulty)
assert.Equal(t, "js", data.DriverName)
assert.Equal(t, int64(1), data.Height)
assert.Equal(t, int64(0), data.Index)
//call rollback
kvset, err = e.ExecDelLocal_Call(call, tx, &types.ReceiptData{Logs: receipt.Logs}, 0)
assert.Nil(t, err)
......
package executor
import "github.com/33cn/chain33/types"
func calcLocalPrefix(execer []byte) []byte {
s := append([]byte("LODB-"), execer...)
s = append(s, byte('-'))
......@@ -12,6 +14,13 @@ func calcStatePrefix(execer []byte) []byte {
return s
}
func calcAllPrefix(name string) ([]byte, []byte) {
execer := types.ExecName("user.js." + name)
state := calcStatePrefix([]byte(execer))
local := calcLocalPrefix([]byte(execer))
return state, local
}
func calcCodeKey(name string) []byte {
return append([]byte("mavl-js-code-"), []byte(name)...)
}
......
package executor
import (
"fmt"
"github.com/33cn/chain33/types"
"github.com/33cn/plugin/plugin/dapp/js/types/jsproto"
)
func (c *js) Query_Query(payload *jsproto.Call) (types.Message, error) {
jsvalue, err := c.callVM("query", payload, nil, 0, nil)
if err != nil {
fmt.Println("query", err)
return nil, err
}
str, err := getString(jsvalue, "result")
if err != nil {
fmt.Println("result", err)
return nil, err
}
return &jsproto.QueryResult{Data: str}, nil
}
......@@ -24,4 +24,8 @@ message JsAction {
message JsLog {
string data = 1;
}
message QueryResult {
string data = 1;
}
\ No newline at end of file
......@@ -3,12 +3,9 @@
package jsproto
import (
fmt "fmt"
math "math"
proto "github.com/golang/protobuf/proto"
)
import proto "github.com/golang/protobuf/proto"
import fmt "fmt"
import math "math"
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
......@@ -34,17 +31,16 @@ func (m *Create) Reset() { *m = Create{} }
func (m *Create) String() string { return proto.CompactTextString(m) }
func (*Create) ProtoMessage() {}
func (*Create) Descriptor() ([]byte, []int) {
return fileDescriptor_d11539bc790542aa, []int{0}
return fileDescriptor_js_054cb5b21912e542, []int{0}
}
func (m *Create) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Create.Unmarshal(m, b)
}
func (m *Create) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Create.Marshal(b, m, deterministic)
}
func (m *Create) XXX_Merge(src proto.Message) {
xxx_messageInfo_Create.Merge(m, src)
func (dst *Create) XXX_Merge(src proto.Message) {
xxx_messageInfo_Create.Merge(dst, src)
}
func (m *Create) XXX_Size() int {
return xxx_messageInfo_Create.Size(m)
......@@ -83,17 +79,16 @@ func (m *Call) Reset() { *m = Call{} }
func (m *Call) String() string { return proto.CompactTextString(m) }
func (*Call) ProtoMessage() {}
func (*Call) Descriptor() ([]byte, []int) {
return fileDescriptor_d11539bc790542aa, []int{1}
return fileDescriptor_js_054cb5b21912e542, []int{1}
}
func (m *Call) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Call.Unmarshal(m, b)
}
func (m *Call) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Call.Marshal(b, m, deterministic)
}
func (m *Call) XXX_Merge(src proto.Message) {
xxx_messageInfo_Call.Merge(m, src)
func (dst *Call) XXX_Merge(src proto.Message) {
xxx_messageInfo_Call.Merge(dst, src)
}
func (m *Call) XXX_Size() int {
return xxx_messageInfo_Call.Size(m)
......@@ -140,17 +135,16 @@ func (m *JsAction) Reset() { *m = JsAction{} }
func (m *JsAction) String() string { return proto.CompactTextString(m) }
func (*JsAction) ProtoMessage() {}
func (*JsAction) Descriptor() ([]byte, []int) {
return fileDescriptor_d11539bc790542aa, []int{2}
return fileDescriptor_js_054cb5b21912e542, []int{2}
}
func (m *JsAction) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_JsAction.Unmarshal(m, b)
}
func (m *JsAction) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_JsAction.Marshal(b, m, deterministic)
}
func (m *JsAction) XXX_Merge(src proto.Message) {
xxx_messageInfo_JsAction.Merge(m, src)
func (dst *JsAction) XXX_Merge(src proto.Message) {
xxx_messageInfo_JsAction.Merge(dst, src)
}
func (m *JsAction) XXX_Size() int {
return xxx_messageInfo_JsAction.Size(m)
......@@ -290,17 +284,16 @@ func (m *JsLog) Reset() { *m = JsLog{} }
func (m *JsLog) String() string { return proto.CompactTextString(m) }
func (*JsLog) ProtoMessage() {}
func (*JsLog) Descriptor() ([]byte, []int) {
return fileDescriptor_d11539bc790542aa, []int{3}
return fileDescriptor_js_054cb5b21912e542, []int{3}
}
func (m *JsLog) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_JsLog.Unmarshal(m, b)
}
func (m *JsLog) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_JsLog.Marshal(b, m, deterministic)
}
func (m *JsLog) XXX_Merge(src proto.Message) {
xxx_messageInfo_JsLog.Merge(m, src)
func (dst *JsLog) XXX_Merge(src proto.Message) {
xxx_messageInfo_JsLog.Merge(dst, src)
}
func (m *JsLog) XXX_Size() int {
return xxx_messageInfo_JsLog.Size(m)
......@@ -318,29 +311,69 @@ func (m *JsLog) GetData() string {
return ""
}
type QueryResult struct {
Data string `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *QueryResult) Reset() { *m = QueryResult{} }
func (m *QueryResult) String() string { return proto.CompactTextString(m) }
func (*QueryResult) ProtoMessage() {}
func (*QueryResult) Descriptor() ([]byte, []int) {
return fileDescriptor_js_054cb5b21912e542, []int{4}
}
func (m *QueryResult) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_QueryResult.Unmarshal(m, b)
}
func (m *QueryResult) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_QueryResult.Marshal(b, m, deterministic)
}
func (dst *QueryResult) XXX_Merge(src proto.Message) {
xxx_messageInfo_QueryResult.Merge(dst, src)
}
func (m *QueryResult) XXX_Size() int {
return xxx_messageInfo_QueryResult.Size(m)
}
func (m *QueryResult) XXX_DiscardUnknown() {
xxx_messageInfo_QueryResult.DiscardUnknown(m)
}
var xxx_messageInfo_QueryResult proto.InternalMessageInfo
func (m *QueryResult) GetData() string {
if m != nil {
return m.Data
}
return ""
}
func init() {
proto.RegisterType((*Create)(nil), "jsproto.Create")
proto.RegisterType((*Call)(nil), "jsproto.Call")
proto.RegisterType((*JsAction)(nil), "jsproto.JsAction")
proto.RegisterType((*JsLog)(nil), "jsproto.JsLog")
}
func init() { proto.RegisterFile("js.proto", fileDescriptor_d11539bc790542aa) }
var fileDescriptor_d11539bc790542aa = []byte{
// 215 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x4c, 0x8f, 0xcf, 0x4e, 0x86, 0x30,
0x10, 0xc4, 0x3f, 0x90, 0x7f, 0x2e, 0x51, 0x93, 0x9e, 0x88, 0x5e, 0x4c, 0xbd, 0xe8, 0x85, 0x18,
0x7c, 0x02, 0xe5, 0x42, 0x88, 0xa7, 0xbe, 0xc1, 0x5a, 0x2a, 0x91, 0x54, 0x6a, 0x68, 0x31, 0xe1,
0xed, 0x4d, 0x17, 0x84, 0xef, 0xf6, 0xeb, 0xce, 0x74, 0x67, 0x16, 0xb2, 0xc1, 0x96, 0x3f, 0x93,
0x71, 0x86, 0xa5, 0x83, 0x25, 0xe0, 0xcf, 0x90, 0xd4, 0x93, 0x42, 0xa7, 0x18, 0x83, 0x48, 0x9a,
0x4e, 0x15, 0xc1, 0x7d, 0xf0, 0x78, 0x29, 0x88, 0xfd, 0x6c, 0xc4, 0x6f, 0x55, 0x84, 0xeb, 0xcc,
0x33, 0x6f, 0x21, 0xaa, 0x51, 0xeb, 0x5d, 0x0b, 0x0e, 0x8d, 0xdd, 0x42, 0xf6, 0x39, 0x8f, 0xf2,
0xec, 0xcf, 0xfe, 0xf6, 0x7e, 0x9c, 0x7a, 0x5b, 0x5c, 0xac, 0x7e, 0xcf, 0xdc, 0x42, 0xd6, 0xda,
0x57, 0xe9, 0xbe, 0xcc, 0xc8, 0x9e, 0x20, 0x91, 0xd4, 0x84, 0x36, 0xe6, 0xd5, 0x4d, 0xb9, 0x75,
0x2c, 0xd7, 0x82, 0xcd, 0x49, 0x6c, 0x06, 0xf6, 0x00, 0x91, 0x44, 0xad, 0x29, 0x22, 0xaf, 0xae,
0x0e, 0x23, 0x6a, 0xdd, 0x9c, 0x04, 0x89, 0xec, 0x1a, 0x42, 0xb7, 0x50, 0x5a, 0x2c, 0x42, 0xb7,
0xbc, 0xa5, 0x10, 0xff, 0xa2, 0x9e, 0x15, 0xbf, 0x83, 0xb8, 0xb5, 0xef, 0xa6, 0xf7, 0x8d, 0x3a,
0x74, 0xf8, 0x7f, 0x81, 0xe7, 0x8f, 0x84, 0x36, 0xbd, 0xfc, 0x05, 0x00, 0x00, 0xff, 0xff, 0xdb,
0x0d, 0xa5, 0xc0, 0x2b, 0x01, 0x00, 0x00,
proto.RegisterType((*QueryResult)(nil), "jsproto.QueryResult")
}
func init() { proto.RegisterFile("js.proto", fileDescriptor_js_054cb5b21912e542) }
var fileDescriptor_js_054cb5b21912e542 = []byte{
// 231 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x8f, 0xbf, 0x4e, 0xc3, 0x30,
0x10, 0xc6, 0x9b, 0x90, 0xa4, 0xe1, 0x22, 0x40, 0xf2, 0x14, 0xc1, 0x02, 0x66, 0x81, 0x25, 0x42,
0xe5, 0x09, 0xa0, 0x4b, 0x15, 0xb1, 0xe0, 0x37, 0x38, 0x5c, 0x53, 0x51, 0x1d, 0x31, 0xf2, 0x1f,
0xa4, 0xbc, 0x3d, 0xf2, 0xa5, 0x34, 0x0c, 0x6c, 0x3f, 0xdf, 0xfd, 0xec, 0xef, 0x33, 0xd4, 0x7b,
0xdf, 0x7d, 0x39, 0x1b, 0xac, 0x58, 0xee, 0x3d, 0x83, 0x7c, 0x80, 0x6a, 0xed, 0x0c, 0x06, 0x23,
0x04, 0x14, 0xda, 0x6e, 0x4d, 0x9b, 0x5d, 0x67, 0x77, 0xa7, 0x8a, 0x39, 0xcd, 0x06, 0xfc, 0x34,
0x6d, 0x3e, 0xcd, 0x12, 0xcb, 0x1e, 0x8a, 0x35, 0x12, 0x1d, 0x77, 0xd9, 0xbc, 0x13, 0x97, 0x50,
0xbf, 0xc7, 0x41, 0xff, 0xb9, 0x73, 0x3c, 0x27, 0x1f, 0xdd, 0xce, 0xb7, 0x27, 0x93, 0x9f, 0x58,
0x7a, 0xa8, 0x7b, 0xff, 0xa4, 0xc3, 0x87, 0x1d, 0xc4, 0x3d, 0x54, 0x9a, 0x9b, 0xf0, 0x8b, 0xcd,
0xea, 0xa2, 0x3b, 0x74, 0xec, 0xa6, 0x82, 0x9b, 0x85, 0x3a, 0x08, 0xe2, 0x16, 0x0a, 0x8d, 0x44,
0x1c, 0xd1, 0xac, 0xce, 0x66, 0x11, 0x89, 0x36, 0x0b, 0xc5, 0x4b, 0x71, 0x0e, 0x79, 0x18, 0x39,
0xad, 0x54, 0x79, 0x18, 0x9f, 0x97, 0x50, 0x7e, 0x23, 0x45, 0x23, 0xaf, 0xa0, 0xec, 0xfd, 0x8b,
0xdd, 0xa5, 0x46, 0x5b, 0x0c, 0xf8, 0xfb, 0x83, 0xc4, 0xf2, 0x06, 0x9a, 0xd7, 0x68, 0xdc, 0xa8,
0x8c, 0x8f, 0x14, 0xfe, 0x53, 0xde, 0x2a, 0x0e, 0x7b, 0xfc, 0x09, 0x00, 0x00, 0xff, 0xff, 0xdf,
0xa4, 0x90, 0x55, 0x4e, 0x01, 0x00, 0x00,
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment