Skip to content

GitLab

  • Projects
  • Groups
  • Snippets
  • Help
    • Loading...
  • Sign in
issue
issue
  • Project overview
    • Project overview
    • Details
    • Activity
    • Releases
  • Repository
    • Repository
    • Files
    • Commits
    • Branches
    • Tags
    • Contributors
    • Graph
    • Compare
  • Issues 23
    • Issues 23
    • List
    • Boards
    • Labels
    • Service Desk
    • Milestones
  • Wiki
    • Wiki
  • Snippets
    • Snippets
  • Members
    • Members
  • Activity
  • Graph
  • Create a new issue
  • Commits
  • Issue Boards
Collapse sidebar

新注册的用户请输入邮箱并保存,随后登录邮箱激活账号。后续可直接使用邮箱登录!

  • chainmaker
  • issueissue
  • Issues
  • #1333

Closed
Open
Opened Mar 11, 2025 by zhen yan@sdjasj2 of 19 tasks completed2/19 tasks

关于并行写golang合约状态变量的疑问

【问题分类】

  • bug
  • P2P网络相关(包含libp2p,liquid)
  • 链账户身份与权限相关(证书问题、public、多签投票问题)
  • 核心交易引擎相关(交易池、DAG)
  • 共识相关
  • 智能合约相关
  • 存储相关
  • SDK相关
  • 长安链CMC工具
  • 长安链管理台
  • 长安链浏览器
  • 长安链合约IDE
  • 长安链web签名插件
  • 跨链相关
  • 轻节点相关
  • 隐私计算相关
  • 密码学相关
  • 环境依赖
  • 其他补充:

【问题描述】(请对问题进行描述,方便定位问题)

package main

import (
	"log"
	"strconv"
	"sync"

	"chainmaker.org/chainmaker/contract-sdk-go/v2/pb/protogo"
	"chainmaker.org/chainmaker/contract-sdk-go/v2/sandbox"
	"chainmaker.org/chainmaker/contract-sdk-go/v2/sdk"
)

type MyContract struct {
	x int
	sync.Mutex
}

func (f *MyContract) InitContract() protogo.Response {
	return sdk.Success([]byte("Init success"))
}

func (f *MyContract) UpgradeContract() protogo.Response {
	return sdk.Success([]byte("Upgrade success"))
}

func (f *MyContract) InvokeContract(method string) protogo.Response {

	switch method {
	case "add":
		return f.Add()
	case "query":
		return f.Query()
	default:
		return sdk.Error("invalid method")
	}
}

func (f *MyContract) Add() protogo.Response {
	f.Lock()
	defer f.Unlock()
	f.x += 1
	return sdk.Success(nil)

}

func (f *MyContract) Query() protogo.Response {
	f.Lock()
	defer f.Unlock()
	return sdk.Success([]byte(strconv.Itoa(f.x)))
}

func main() {
	err := sandbox.Start(new(MyContract))
	if err != nil {
		log.Fatal(err)
	}
}

上面这是我在长安链上部署的合约,合约存在一个int变量x和一个互斥锁,Add()方法会加锁并自增一次x,随后我编写了如下代码对这个合约的Add()方法进行并行调用100次,并在调用前后进行x值的查询,在Add()方法调用后会sleep三分钟再进行查询

package demo

import (
	"chainmaker-parallel/client"
	"chainmaker-parallel/logger"
	"chainmaker.org/chainmaker/pb-go/v2/common"
	"fmt"
	"log"
	"sync"
	"time"
)

func main() {
	logger.Init()
	client.Init()
	contractname := "contract_name"
	fmt.Println("====================== 创建合约 ======================")
	resp, err := client.CreateUserContractGOLANG(client.Client, contractname, "1.0.0",
		"/root/chainmaker-parallel/test/contract_name.7z", common.RuntimeType_DOCKER_GO, []*common.KeyValuePair{}, true)
	if err != nil {
		log.Fatalln(err)
	}

	if err != nil {
		logger.Logger.Panic(err)
	}
	fmt.Println(resp)
	kvs := []*common.KeyValuePair{
		{
			Key:   "method",
			Value: []byte("add"),
		},
	}
	fmt.Println("====================== 调用合约 ======================")
	var wg sync.WaitGroup
	wg.Add(100)
	for i := 0; i < 100; i++ {
		go func() {
			defer wg.Done()
			_, err := client.InvokeUserContractWithResult(client.Client, contractname, "invoke_contract", "", kvs, true)
			if err != nil {
				fmt.Println(err)
			}
		}()
	}
	wg.Wait()
	fmt.Println("Sleep for 3min")
	time.Sleep(time.Minute * 3)
	fmt.Println("====================== 执行合约查询接口 ======================")
	kvs = []*common.KeyValuePair{
		{
			Key:   "method",
			Value: []byte("query"),
		},
	}
	client.UserContractClaimQuery(client.Client, contractname, "invoke_contract", kvs)
}

但是输出结果如下,可以看到调用合约的时候所有交易的结果都没有出错,表示交易成功执行了,但是合约x的值却只增加了1,而不是在所有交易结果都没有出错情况下的100。我猜测可能是因为访问冲突变量导致了交易回滚,但是这样的话交易返回的结果应该含有err,不知道是哪里出了问题

====================== 创建合约 ======================
contract_result:<result:"\n\016contract_name3\022\0051.0.0\030\010*\236\010\n\026wx-org1.chainmaker.org\032\224\007-----BEGIN CERTIFICATE-----\nMIICeDCCAh6gAwIBAgIDDInJMAoGCCqGSM49BAMCMIGKMQswCQYDVQQGEwJDTjEQ\nMA4GA1UECBMHQmVpamluZzEQMA4GA1UEBxMHQmVpamluZzEfMB0GA1UEChMWd3gt\nb3JnMS5jaGFpbm1ha2VyLm9yZzESMBAGA1UECxMJcm9vdC1jZXJ0MSIwIAYDVQQD\nExljYS53eC1vcmcxLmNoYWlubWFrZXIub3JnMB4XDTI1MDMxMTAxNTMyOVoXDTMw\nMDMxMDAxNTMyOVowgZExCzAJBgNVBAYTAkNOMRAwDgYDVQQIEwdCZWlqaW5nMRAw\nDgYDVQQHEwdCZWlqaW5nMR8wHQYDVQQKExZ3eC1vcmcxLmNoYWlubWFrZXIub3Jn\nMQ8wDQYDVQQLEwZjbGllbnQxLDAqBgNVBAMTI2NsaWVudDEuc2lnbi53eC1vcmcx\nLmNoYWlubWFrZXIub3JnMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAETytUSR2Y\nsE1cOzxntj51E1A8P/hanTznuGzAKlnM8VcROMzdA5nPKfsoL4/ekbpFHirTKmXo\n/g3mK9pKnxl4A6NqMGgwDgYDVR0PAQH/BAQDAgbAMCkGA1UdDgQiBCCI/G1PwSA/\nsuSy4jjOoimRowx9sd0oDIAxPwWnNBSqJzArBgNVHSMEJDAigCDAPJNN+V+E4XD0\nHiDgtaGh3M6WNemT0uWKaJrh745rqDAKBggqhkjOPQQDAgNIADBFAiAudGlMXCRp\nQDGHcvX8HTqtmwjM9nJQIi9284Bd79aBswIhAKgHLI12CWYU46lqwDarvGU1blIJ\nwPrDS35uxAfzqsqU\n-----END CERTIFICATE-----\n\"#client1.sign.wx-org1.chainmaker.org*\006CLIENT2@88fc6d4fc1203fb2e4b2e238cea22991a30c7db1dd280c80313f05a73414aa272(3943da63d19ac656e6f1fc31f02b440dea0107b3" message:"Success" gas_used:13146 > tx_id:"182b9dcb19506ef2cacbda7fe32bf6bbc1524d3edbf84bb2a8597a58d8c03943" tx_timestamp:1741659176 tx_block_height:5 
====================== 合约调用前变量取值 ======================
QUERY claim contract resp: message:"SUCCESS" contract_result:<result:"0" message:"Success" gas_used:111 > tx_id:"182b9dcba4df08c0caff0ef95a7ac9b6fd2e052ad4ed4af0bb92f2c949418179" 
====================== 调用合约 ======================
Sleep for 3min
====================== 合约调用后变量取值 ======================
QUERY claim contract resp: message:"SUCCESS" contract_result:<result:"1" message:"Success" gas_used:111 > tx_id:"182b9df5c49b9c6acad517

下面这是调用合约的代码,仅当交易成功时才不会返回error

func InvokeUserContractWithResult(client *sdk.ChainClient, contractName, method, txId string,
	kvs []*common.KeyValuePair, withSyncResult bool) ([]byte, error) {

	resp, err := client.InvokeContract(contractName, method, txId, kvs, -1, withSyncResult)
	if err != nil {
		return nil, err
	}

	if resp.Code != common.TxStatusCode_SUCCESS {
		return nil, fmt.Errorf("invoke contract failed, [code:%d]/[msg:%s]", resp.Code, resp.Message)
	}

	return resp.ContractResult.Result, nil
}

【相关日志文件】(如果有报错日志请贴图,或者上传附件)

节点日志 log.tar.gz sdk日志 sdk.log

【系统信息】(请填写系统信息,方便定位问题)

  • chainmaker-go version * : [v2.3.4]
  • OS & version * : ubuntu-22.04.3-live-server-amd64, Linux version 5.15.0-119-generic
Edited Mar 13, 2025 by zhen yan
To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information
Assignee
Assign to
None
Milestone
None
Assign milestone
Time tracking
None
Due date
None
Reference: chainmaker/issue#1333

Copyright © 2021 ChainMaker Org. All Rights Reserved. 长安链 版权所有。