func UpdatePermission(targetUser proto.AccountAddress,
	targetChain proto.AccountAddress, perm *types.UserPermission) (txHash hash.Hash, err error) {
	if atomic.LoadUint32(&driverInitialized) == 0 {
		err = ErrNotInitialized
		return
	}
	var (
		pubKey  *asymmetric.PublicKey
		privKey *asymmetric.PrivateKey
		addr    proto.AccountAddress
		nonce   interfaces.AccountNonce
	)
	if pubKey, err = kms.GetLocalPublicKey(); err != nil {
		return
	}
	if privKey, err = kms.GetLocalPrivateKey(); err != nil {
		return
	}
	if addr, err = crypto.PubKeyHash(pubKey); err != nil {
		return
	}
	nonce, err = getNonce(addr)
	if err != nil {
		return
	}
	up := types.NewUpdatePermission(&types.UpdatePermissionHeader{
		TargetSQLChain: targetChain,
		TargetUser:     targetUser,
		Permission:     perm,
		Nonce:          nonce,
	})
	err = up.Sign(privKey)
	if err != nil {
		log.WithError(err).Warning("sign failed")
		return
	}
	addTxReq := new(types.AddTxReq)
	addTxResp := new(types.AddTxResp)
	addTxReq.Tx = up
	err = requestBP(route.MCCAddTx, addTxReq, addTxResp)
	if err != nil {
		log.WithError(err).Warning("send tx failed")
		return
	}
	txHash = up.Hash()
	return
}
UpdatePermission sends UpdatePermission transaction to chain.
package client
import (
	"context"
	"database/sql"
	"database/sql/driver"
	"fmt"
	"math/rand"
	"strings"
	"sync"
	"sync/atomic"
	"time"
	"github.com/pkg/errors"
	bp "github.com/SQLess/SQLess/blockproducer"
	"github.com/SQLess/SQLess/blockproducer/interfaces"
	"github.com/SQLess/SQLess/conf"
	"github.com/SQLess/SQLess/crypto"
	"github.com/SQLess/SQLess/crypto/asymmetric"
	"github.com/SQLess/SQLess/crypto/hash"
	"github.com/SQLess/SQLess/crypto/kms"
	"github.com/SQLess/SQLess/proto"
	"github.com/SQLess/SQLess/route"
	rpc "github.com/SQLess/SQLess/rpc/mux"
	"github.com/SQLess/SQLess/types"
	"github.com/SQLess/SQLess/utils"
	"github.com/SQLess/SQLess/utils/log"
)
const (
	DBScheme = "cqlprotocol"
	DBSchemeAlias = "cql"
	DefaultGasPrice = 1
	DefaultAdvancePayment = 20000000
)
var (
	PeersUpdateInterval = time.Second * 5
	driverInitialized   uint32
	peersUpdaterRunning uint32
	peerList            sync.Map  
	connIDLock          sync.Mutex
	connIDAvail         []uint64
	globalSeqNo         uint64
	randSource          = rand.New(rand.NewSource(time.Now().UnixNano()))
	DefaultConfigFile = "~/.cql/config.yaml"
)
func init() {
	d := new(covenantSQLDriver)
	sql.Register(DBScheme, d)
	sql.Register(DBSchemeAlias, d)
	log.Debug("SQLess driver registered.")
}
type covenantSQLDriver struct {
}
func (d *covenantSQLDriver) Open(dsn string) (conn driver.Conn, err error) {
	var cfg *Config
	if cfg, err = ParseDSN(dsn); err != nil {
		return
	}
	if atomic.LoadUint32(&driverInitialized) == 0 {
		err = defaultInit()
		if err != nil && err != ErrAlreadyInitialized {
			return
		}
	}
	return newConn(cfg)
}
type ResourceMeta struct {
	TargetMiners           []proto.AccountAddress `json:"target-miners,omitempty"`         
	Node                   uint16                 `json:"node,omitempty"`                  
	Space                  uint64                 `json:"space,omitempty"`                 
	Memory                 uint64                 `json:"memory,omitempty"`                
	LoadAvgPerCPU          float64                `json:"load-avg-per-cpu,omitempty"`      
	EncryptionKey          string                 `json:"encrypt-key,omitempty"`           
	UseEventualConsistency bool                   `json:"eventual-consistency,omitempty"`  
	ConsistencyLevel       float64                `json:"consistency-level,omitempty"`     
	IsolationLevel         int                    `json:"isolation-level,omitempty"`       
	GasPrice       uint64 `json:"gas-price"`        
	AdvancePayment uint64 `json:"advance-payment"`  
}
func defaultInit() (err error) {
	configFile := utils.HomeDirExpand(DefaultConfigFile)
	if configFile == DefaultConfigFile {
		log.Debugf("Could not find SQLess default config location: %v", configFile)
		return ErrNotInitialized
	}
	log.Debugf("Using SQLess default config location: %v", configFile)
	return Init(configFile, []byte(""))
}
func Init(configFile string, masterKey []byte) (err error) {
	if !atomic.CompareAndSwapUint32(&driverInitialized, 0, 1) {
		err = ErrAlreadyInitialized
		return
	}
	if conf.GConf, err = conf.LoadConfig(configFile); err != nil {
		return
	}
	route.InitKMS(conf.GConf.PubKeyStoreFile)
	if err = kms.InitLocalKeyPair(conf.GConf.PrivateKeyFile, masterKey); err != nil {
		return
	}
	if err = registerNode(); err != nil {
		return
	}
	if err = runPeerListUpdater(); err != nil {
		return
	}
	return
}
func Create(meta ResourceMeta) (txHash hash.Hash, dsn string, err error) {
	if atomic.LoadUint32(&driverInitialized) == 0 {
		err = ErrNotInitialized
		return
	}
	var (
		nonceReq   = new(types.NextAccountNonceReq)
		nonceResp  = new(types.NextAccountNonceResp)
		req        = new(types.AddTxReq)
		resp       = new(types.AddTxResp)
		privateKey *asymmetric.PrivateKey
		clientAddr proto.AccountAddress
	)
	if privateKey, err = kms.GetLocalPrivateKey(); err != nil {
		err = errors.Wrap(err, "get local private key failed")
		return
	}
	if clientAddr, err = crypto.PubKeyHash(privateKey.PubKey()); err != nil {
		err = errors.Wrap(err, "get local account address failed")
		return
	}
	nonceReq.Addr = clientAddr
	if err = requestBP(route.MCCNextAccountNonce, nonceReq, nonceResp); err != nil {
		err = errors.Wrap(err, "allocate create database transaction nonce failed")
		return
	}
	if meta.GasPrice == 0 {
		meta.GasPrice = DefaultGasPrice
	}
	if meta.AdvancePayment == 0 {
		meta.AdvancePayment = DefaultAdvancePayment
	}
	req.TTL = 1
	req.Tx = types.NewCreateDatabase(&types.CreateDatabaseHeader{
		Owner: clientAddr,
		ResourceMeta: types.ResourceMeta{
			TargetMiners:           meta.TargetMiners,
			Node:                   meta.Node,
			Space:                  meta.Space,
			Memory:                 meta.Memory,
			LoadAvgPerCPU:          meta.LoadAvgPerCPU,
			EncryptionKey:          meta.EncryptionKey,
			UseEventualConsistency: meta.UseEventualConsistency,
			ConsistencyLevel:       meta.ConsistencyLevel,
			IsolationLevel:         meta.IsolationLevel,
		},
		GasPrice:       meta.GasPrice,
		AdvancePayment: meta.AdvancePayment,
		TokenType:      types.Particle,
		Nonce:          nonceResp.Nonce,
	})
	if err = req.Tx.Sign(privateKey); err != nil {
		err = errors.Wrap(err, "sign request failed")
		return
	}
	if err = requestBP(route.MCCAddTx, req, resp); err != nil {
		err = errors.Wrap(err, "call create database transaction failed")
		return
	}
	txHash = req.Tx.Hash()
	cfg := NewConfig()
	cfg.DatabaseID = string(proto.FromAccountAndNonce(clientAddr, uint32(nonceResp.Nonce)))
	dsn = cfg.FormatDSN()
	return
}
func WaitDBCreation(ctx context.Context, dsn string) (err error) {
	dsnCfg, err := ParseDSN(dsn)
	if err != nil {
		return
	}
	db, err := sql.Open("cqlprotocol", dsn)
	defer db.Close()
	if err != nil {
		return
	}
	err = WaitBPDatabaseCreation(ctx, proto.DatabaseID(dsnCfg.DatabaseID), db, 3*time.Second)
	return
}
func WaitBPDatabaseCreation(
	ctx context.Context,
	dbID proto.DatabaseID,
	db *sql.DB,
	period time.Duration,
) (err error) {
	var (
		ticker = time.NewTicker(period)
		req    = &types.QuerySQLChainProfileReq{
			DBID: dbID,
		}
		count = 0
	)
	defer ticker.Stop()
	defer fmt.Printf("\n")
	for {
		select {
		case <-ticker.C:
			count++
			if err = rpc.RequestBP(
				route.MCCQuerySQLChainProfile.String(), req, nil,
			); err != nil {
				if !strings.Contains(err.Error(), bp.ErrDatabaseNotFound.Error()) {
					return
				}
			} else {
				if db == nil {
					return
				}
				if _, err = db.ExecContext(ctx, "SHOW TABLES"); err == nil {
					return
				}
			}
			fmt.Printf("\rQuerying SQLChain Profile %vs", count*int(period.Seconds()))
		case <-ctx.Done():
			if err != nil {
				return errors.Wrapf(ctx.Err(), "last error: %s", err.Error())
			}
			return ctx.Err()
		}
	}
}
func Drop(dsn string) (txHash hash.Hash, err error) {
	if atomic.LoadUint32(&driverInitialized) == 0 {
		err = ErrNotInitialized
		return
	}
	var cfg *Config
	if cfg, err = ParseDSN(dsn); err != nil {
		return
	}
	peerList.Delete(cfg.DatabaseID)
	return
}
func GetTokenBalance(tt types.TokenType) (balance uint64, err error) {
	if atomic.LoadUint32(&driverInitialized) == 0 {
		err = ErrNotInitialized
		return
	}
	req := new(types.QueryAccountTokenBalanceReq)
	resp := new(types.QueryAccountTokenBalanceResp)
	var pubKey *asymmetric.PublicKey
	if pubKey, err = kms.GetLocalPublicKey(); err != nil {
		return
	}
	if req.Addr, err = crypto.PubKeyHash(pubKey); err != nil {
		return
	}
	req.TokenType = tt
	if err = requestBP(route.MCCQueryAccountTokenBalance, req, resp); err == nil {
		if !resp.OK {
			err = ErrNoSuchTokenBalance
			return
		}
		balance = resp.Balance
	}
	return
}