
# 1.需求描述  
遍历某个地址在合约中的所有余额，并列出有币合约的合约地址和frozen和active的值。

存储格式mavl-coins-bty-exec-exec_addr-addr，例如：mavl-coins-bty-exec-16htvcBNSEA7fZhAdLJphDwQRQJaHpyHTp:1JmFaA6unrCFYEWPGRi7uuXY1KthTJxJEP，16htvcBNSEA7fZhAdLJphDwQRQJaHpyHTp为合约地址，1JmFaA6unrCFYEWPGRi7uuXY1KthTJxJEP为具体账户地址。

# 2.输入参数  
![输入参数](./resource/exec-balance-param.png) 

样例：  
	cli exec_balance -symbol bty -exec coins -addr 1JmFaA6unrCFYEWPGRi7uuXY1KthTJxJEP -exec_addr 16htvcBNSEA7fZhAdLJphDwQRQJaHpyHTp -height -1

可简写为：  
	cli exec_balance -addr 1JmFaA6unrCFYEWPGRi7uuXY1KthTJxJEP -exec_addr 16htvcBNSEA7fZhAdLJphDwQRQJaHpyHTp  

如果查询地址在所有合约中的余额，则可简写为：    
	cli exec_balance -addr 1JmFaA6unrCFYEWPGRi7uuXY1KthTJxJEP  

# 3.输出结果  
	{
		"totalAmount":10,
		"frozenAmount":3,
		"activeAmount":7,
		"execBalances":[
     		{
				"execAddr":"xxxx",
				"frozen":1,
				"active":2
			},
			...
		]
	} 


# 4.调用过程及处理逻辑   

![输入参数](./resource/exec-balance-query.png) 

ReplyGetExecBalance.IterateExecBalanceByStateHash中的处理逻辑如下：  

	合法的key例子：mavl-coins-bty-exec-16htvcBNSEA7fZhAdLJphDwQRQJaHpyHTp:1JmFaA6unrCFYEWPGRi7uuXY1KthTJxJEP
	对于遍历时遇到的key和value进行如下处理：
    1.如果查询请求中的前缀以：结尾（比如为“mavl-coins-bty-exec-16htvcBNSEA7fZhAdLJphDwQRQJaHpyHTp:”），说明携带了具体合约地址，则一个用户地址对应该合约有一条记录，处理完成即可返回结果。  
    2.如果查询请求中未以：结尾（比如为“mavl-coins-bty-exec-”），说明未携带具体合约地址，需要遍历所有合约地址，对于用户地址相符的记录进行解码处理，并统计余额及记录合约地址和该合约上的余额信息，直到遍历完成。

<font color=#FF0000>
注意：  
由于存储合约余额的格式中，用户地址在后，变化的合约地址在前，导致处理上不是很直接，避免不了遍历所有合约及用户地址，在性能上需要注意。
</font>

附：主要的代码逻辑：  

	func (t *ReplyGetExecBalance) IterateExecBalanceByStateHash(key, value []byte) bool {
		//合法的key例子：mavl-coins-bty-exec-16htvcBNSEA7fZhAdLJphDwQRQJaHpyHTp:1JmFaA6unrCFYEWPGRi7uuXY1KthTJxJEP
		strKey := string(key)
		strPrefix := string(t.Prefix)
		fmt.Println("ReplyGetExecBalance.IterateExecBalanceByStateHash", "key", strKey)
	    if !strings.HasPrefix(strKey, strPrefix) {
			tlog.Error("ReplyGetExecBalance.IterateExecBalanceByStateHash key does not match prefix", "key", strKey, "prefix", strPrefix)
			return true
		}
		//如果prefix形如：mavl-coins-bty-exec-16htvcBNSEA7fZhAdLJphDwQRQJaHpyHTp:  ,则是查找addr在一个合约地址上的余额，找到一个值即可结束。
		if strings.HasSuffix(strPrefix, ":") {
			addr := strKey[len(strPrefix):]
			if addr == string(t.Addr) {
				execAddr := []byte(strPrefix[(len(strPrefix) - len(addr) - 1):(len(strPrefix) - 1)])
				t.addItem(execAddr, value)
				return true
			} else {
				return false
			}
		} else {
			combinAddr := strKey[len(strPrefix):]
			addrs := strings.Split(combinAddr, ":")
			if 2 != len(addrs) {
				tlog.Error("ReplyGetExecBalance.IterateExecBalanceByStateHash key does not contain exec-addr & addr", "key", strKey, "combinAddr", combinAddr)
				return true
			}
	
			if addrs[1] != string(t.Addr) {
				return false
			}
			t.addItem([]byte(addrs[0]), value)
			return false
		}
	}

	func (t *ReplyGetExecBalance) addItem(execAddr, value []byte) {
		var acc Account
		err := Decode(value, &acc)
		if err != nil {
			tlog.Error("ReplyGetExecBalance.IterateExecBalanceByStateHash", "err", err)
			return
		}
		//tlog.Info("acc:", "value", acc)
		t.Amount += acc.Balance
		t.Amount += acc.Frozen
	
		t.AmountActive += acc.Balance
		t.AmountFrozen += acc.Frozen
	
		item := &ExecBalanceItem{execAddr, acc.Frozen, acc.Balance}
		t.Items = append(t.Items, item)
	}  

附：相关UT用例：  
TestIterateRangeForExecBalance    所在文件mavl_test.go  
TestIterateExecBalanceByStateHash_PrefixWithoutExecAddr  所在文件types_test.go  
TestIterateExecBalanceByStateHash_PrefixWithExecAddr     所在文件types_test.go