func (s *StateDB) BatchGet(keys [][]byte) (values [][]byte, err error) {
	for _, key := range keys {
		v, err := s.Get(key)
		if err != nil && err != types.ErrNotFound {
			return nil, err
		}
		values = append(values, v)
	}
	return values, nil
}
BatchGet batch get keys from state db
package executor
import (
	"encoding/hex"
	"github.com/33cn/chain33/common/db"
	"github.com/33cn/chain33/queue"
	"github.com/33cn/chain33/types"
)
type StateDB struct {
	cache     map[string][]byte
	txcache   map[string][]byte
	keys      []string
	intx      bool
	client    queue.Client
	stateHash []byte
	version   int64
	height    int64
	local     *db.SimpleMVCC
	opt       *StateDBOption
}
type StateDBOption struct {
	EnableMVCC bool
	Height     int64
}
func NewStateDB(client queue.Client, stateHash []byte, localdb db.KVDB, opt *StateDBOption) db.KV {
	if opt == nil {
		opt = &StateDBOption{}
	}
	db := &StateDB{
		cache:     make(map[string][]byte, 1),
		txcache:   make(map[string][]byte),
		intx:      false,
		client:    client,
		stateHash: stateHash,
		height:    opt.Height,
		version:   -1,
		local:     db.NewSimpleMVCC(localdb),
		opt:       opt,
	}
	return db
}
func (s *StateDB) enableMVCC(hash []byte) {
	opt := s.opt
	if opt.EnableMVCC {
		if hash == nil {
			hash = s.stateHash
		}
		v, err := s.local.GetVersion(hash)
		if err == nil && v >= 0 {
			s.version = v
		} else if s.height > 0 {
			println("init state db", "height", s.height, "err", err.Error(), "v", v, "stateHash", hex.EncodeToString(s.stateHash))
			panic("mvcc get version error,config set enableMVCC=true, it must be synchronized from 0 height")
		}
	}
}
func (s *StateDB) Begin() {
	s.intx = true
	s.keys = nil
	types.AssertConfig(s.client)
	cfg := s.client.GetConfig()
	if cfg.IsFork(s.height, "ForkExecRollback") {
		s.txcache = nil
	}
}
func (s *StateDB) Rollback() {
	s.resetTx()
}
func (s *StateDB) Commit() error {
	for k, v := range s.txcache {
		s.cache[k] = v
	}
	s.intx = false
	s.keys = nil
	types.AssertConfig(s.client)
	cfg := s.client.GetConfig()
	if cfg.IsFork(s.height, "ForkExecRollback") {
		s.resetTx()
	}
	return nil
}
func (s *StateDB) resetTx() {
	s.intx = false
	s.txcache = nil
	s.keys = nil
}
func (s *StateDB) Get(key []byte) ([]byte, error) {
	v, err := s.get(key)
	debugAccount("==get==", key, v)
	return v, err
}
func (s *StateDB) get(key []byte) ([]byte, error) {
	skey := types.Bytes2Str(key)
	if s.intx && s.txcache != nil {
		if value, ok := s.txcache[skey]; ok {
			return value, nil
		}
	}
	if value, ok := s.cache[skey]; ok {
		return value, nil
	}
	if s.version >= 0 {
		data, err := s.local.GetV(key, s.version)
		return data, err
	}
	if s.client == nil {
		return nil, types.ErrNotFound
	}
	query := &types.StoreGet{StateHash: s.stateHash, Keys: [][]byte{key}}
	msg := s.client.NewMessage("store", types.EventStoreGet, query)
	err := s.client.Send(msg, true)
	if err != nil {
		return nil, err
	}
	resp, err := s.client.Wait(msg)
	if err != nil {
		panic(err)  
	}
	defer s.client.FreeMessage(msg, resp)
	if nil == resp.GetData().(*types.StoreReplyValue).Values {
		return nil, types.ErrNotFound
	}
	value := resp.GetData().(*types.StoreReplyValue).Values[0]
	if value == nil {
		return nil, types.ErrNotFound
	}
	s.cache[string(key)] = value
	return value, nil
}
func debugAccount(prefix string, key []byte, value []byte) {
}
func (s *StateDB) StartTx() {
	s.keys = nil
}
func (s *StateDB) GetSetKeys() (keys []string) {
	return s.keys
}
func (s *StateDB) Set(key []byte, value []byte) error {
	debugAccount("==set==", key, value)
	skey := string(key)
	if s.intx {
		if s.txcache == nil {
			s.txcache = make(map[string][]byte)
		}
		s.keys = append(s.keys, skey)
		setmap(s.txcache, skey, value)
	} else {
		setmap(s.cache, skey, value)
	}
	return nil
}
func setmap(data map[string][]byte, key string, value []byte) {
	if value == nil {
		delete(data, key)
		return
	}
	data[key] = value
}