Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multisig transaction validation issues #4788

Closed
ekko-Huang opened this issue Jan 19, 2025 · 16 comments
Closed

Multisig transaction validation issues #4788

ekko-Huang opened this issue Jan 19, 2025 · 16 comments
Labels
t:bug Type: This doesn't seem right.

Comments

@ekko-Huang
Copy link

priList := string{
“0x84b046985a5b41765da29ed563ab8415fd3d15a92d6a1401456b921e68712ab2”,
“0xdc5607ddcb3fc3628aeee78019264f6554b386848387cdfb86f929f5be5e3153”,
“0xeef442bdeea83071b9c6973a7d83fe85d8f6268f8b054166fe6f5e0d0cabf67d”,
“0x0630ac9b0e0e7f6763b6c9086a8643c8525c7330709e0f67ef5d0f716f1beef1”,
“0xc96d00c311c50a39eeef57409a03136b85f99a20209e99a3a87d1acef89684ec”,
“0x1c611b96858b5851d5d14c12b76370f8f6925429ea21532cf664f6eb3df24e9f”,
“0x2e1446d7138be84254f4d99142ebee6bba6f1fe9935e1b8bea545c8cb98fc15f”,
“0x5ca3a929f96d16d6dc52e9a8d588a4f564ff09336e8d4fcf376871a81ea5adcb”,
“0xa71c811d3a573d6ab9a6b53294e97fd859f8d126f9f1e1262b81c94aac02baad”,
“0x8c90e22bb9daae6c89ec0aff180fcbad21085ae2e779b9769fa9e3c7381c3412”,
“0xda32cd017fbbd4a46033fb1cd9e0a6908f2647277391afb09ab820f7dd4d28cb”,
“0xb9835b0699a5db9480b573914ac12d688b0635a13a7315418daa2c0fdcded990”,
“0x0731a36f123ca6f447c4f342cad04373bbe1543b7e4eb1e6a4216e7ad6b4d622”,
“0x4f47a78618820d10d34a10c00869438500d4947dc5315d763fbeb71636f053ce”,
“0x02adedab1bfb830c65f603cb87d17099773909b30ac868ccb3e7554419260acc”,
}

I used these 15 private keys to generate a 9/15 multisig address:
ckt1qpw9q60tppt7l3j7r09qcp7lxnp3vcanvgha8pmvsa3jplykxn32sqvmqu29rxgzm0f7hur9y3xmq87m0nzffxg4dcd30

However, I found that broadcasting the transaction sometimes results in an error:
TransactionFailedToVerify: Verification failed Script(TransactionScriptError { source: Inputs[0].Lock, cause: ValidationFailure: see error code -52 on page secp256k1-blake160-multisig-all (via type hash) | ckb-script-error-codes })

I have checked and all signatures are correct. I don't understand where my problem is. I couldn't find the -52 error code in the CKB node source code. I know this error code means either the signature result didn't provide enough signatures, or the signature and public key verification failed. I also provided the multisigScript in the transaction.

Here is my transaction structure:

{
	"version": "0x0",
	"cell_deps": [{
		"out_point": {
			"tx_hash": "0xf8de3bb47d055cdf460d93a2a6e1b05f7432f9777c8c474abf4eec1d4aee5d37",
			"index": "0x1"
		},
		"dep_type": "dep_group"
	}],
	"header_deps": [],
	"inputs": [{
		"previous_output": {
			"tx_hash": "0x5c134f87fb4922d2cdbbc38ca67a6b0671cfbe0f97a33346aa7d4d1320d341b9",
			"index": "0x1"
		},
		"since": "0x0"
	}],
	"outputs": [{
		"lock": {
			"code_hash": "0x5c5069eb0857efc65e1bca0c07df34c31663b3622fd3876c876320fc9634e2a8",
			"hash_type": "type",
			"args": "0xbc9818d8a149cfc0cd0323386c46ba07920a037f"
		},
		"type": null,
		"capacity": "0x26944023b"
	}, {
		"lock": {
			"code_hash": "0x5c5069eb0857efc65e1bca0c07df34c31663b3622fd3876c876320fc9634e2a8",
			"hash_type": "type",
			"args": "0x9b0714519902dbd3ebf065244db01fdb7cc49499"
		},
		"type": null,
		"capacity": "0x8ff23aa3c76"
	}],
	"outputs_data": ["0x", "0x"],
	"witnesses": ["0x8d030000100000008d0300008d030000790300000000090f9967976c9b63539f4ad831ee6420562cb7107a8ca731774660ff40ae6733217d854fac421beaeed9839e3db1e2a3eef8891c5f7e17b47a3ae375f928f3d5d4d857e062efbf6247a5a5b9c60511377cfdafae61a94a545ec0ce9f770079160eb915766a77cb8c70bb049c4f4890d93de9bd32ee9ae92ef05e79b3e273656dd9b3be2c966d0dbd7bb47d56704e28d838d0fa6dfcde3d35300a6e021f7fba24a53f0147e09f25f43b1fdfcc959832fcd4e540e46dbabe36e17549c24fdc3662831222b1174bb81505e0354c2c288d6c82b8fca6e6fbebc507635bc14409177c9bda2ca6442735430bae597b707dc69fbf5519e891a9f4adae0ec2c0327fb9c22eb35de3a4d9a59320940dffc2be9dd5e23bf02f544e12414edc9a6f431facce8900a789a0d3688955df56193204030d02f3ff990abef82942d5b093f64f1f66d577d23dc11412b98e778d3de56816c0945a97c99fb7c169c01be8a55174e85e5ebb84da5405e83aac98d2e6e49100ea2d44f689ee3c1cc731e6d79911c7ec30bac356a298d516bb371550a5f9cad611cff0ad80b43f5449bdeeb7a0473c21b0a6fa7efe6aada60cff6d6db354d3d5000e0b7f038070c552cd53f92ecc10e29a079f5a861c5f9e5ff0b7f868c480d4d4164fa55db6b0d1bba46dc3406ab48ea70aac8f4ce00ace291af217c6b28ed60c0013252db73ef63c3c7e827e6847ee339beda75d854fef3f5b17249ba51e39398a6265c11cea76a8afedac0b81f7650afebf085bdbec55e01a98e81f31197dc661019a2c0f0d860f0eed9d688e55c90cbb8ff0731ed81168c872126278ca8cc117763c8cbabce21cdcf0d9d1050b3ecd4a408748d1863724764ab7586e48f63e9d5600cba4cb7061003dd4bd177203fbb8318d143e8969eea523b2884ae7df7acc37a6708791422582f5e13a317378f630d019879aa45eba250fea110fa993c9fbdc600153d94f2441aa7ce4ca8ece06794df60f56c2e0ff2a77c66b06437da1c5cbd3c85082222b961be11fc672658f8f542f8b8f537b04ce784d4b050687dba6b64a2c019f8fb606ebf952cd10f1e8bbbc90a5a7fdbe46ba1b1178043b624fdb4a3a073d3d2c4521da01135d208778f7b02b5857a9f7c444b2be0497b806e5af9eead32b013335bc91eb6096717d487ec0071965cc231659b9c3181230c04676551218544f534fdfabd9d0931e1f062c21d2f4ca775c942d68f953af154d5390cbc302caed00"]
}

Environment

SDK: ckb-sdk-go

my code:

func TestTx(t *testing.T) {
	priList := []string{
		"0x84b046985a5b41765da29ed563ab8415fd3d15a92d6a1401456b921e68712ab2",
		"0xdc5607ddcb3fc3628aeee78019264f6554b386848387cdfb86f929f5be5e3153",
		"0xeef442bdeea83071b9c6973a7d83fe85d8f6268f8b054166fe6f5e0d0cabf67d",
		"0x0630ac9b0e0e7f6763b6c9086a8643c8525c7330709e0f67ef5d0f716f1beef1",
		"0xc96d00c311c50a39eeef57409a03136b85f99a20209e99a3a87d1acef89684ec",
		"0x1c611b96858b5851d5d14c12b76370f8f6925429ea21532cf664f6eb3df24e9f",
		"0x2e1446d7138be84254f4d99142ebee6bba6f1fe9935e1b8bea545c8cb98fc15f",
		"0x5ca3a929f96d16d6dc52e9a8d588a4f564ff09336e8d4fcf376871a81ea5adcb",
		"0xa71c811d3a573d6ab9a6b53294e97fd859f8d126f9f1e1262b81c94aac02baad",

		"0x8c90e22bb9daae6c89ec0aff180fcbad21085ae2e779b9769fa9e3c7381c3412",
		"0xda32cd017fbbd4a46033fb1cd9e0a6908f2647277391afb09ab820f7dd4d28cb",
		"0xb9835b0699a5db9480b573914ac12d688b0635a13a7315418daa2c0fdcded990",
		"0x0731a36f123ca6f447c4f342cad04373bbe1543b7e4eb1e6a4216e7ad6b4d622",
		"0x4f47a78618820d10d34a10c00869438500d4947dc5315d763fbeb71636f053ce",
		"0x02adedab1bfb830c65f603cb87d17099773909b30ac868ccb3e7554419260acc",
	}
	client, err := rpc.Dial("https://testnet.ckb.dev")
	if err != nil {
		t.Fatal(err)
	}
	var privMap = make(map[string]string)

	network := types.NetworkTest
	var threshold byte = 9
	multisigConfig := systemscript.NewMultisigConfig(0, threshold)
	for _, pri := range priList {
		pri1, _ := secp256k1.HexToKey(pri)
		blakepub := blake2b.Blake160(pri1.PubKey())
		multisigConfig.AddKeyHash(blakepub)
		privMap[hexutil.Encode(pri1.Bytes())] = hexutil.Encode(blakepub)
	}
	args := multisigConfig.Hash160()
	fmt.Println("multi", len(priList), hexutil.Encode(args), hex.EncodeToString(multisigConfig.Encode()))
	sender, _ := address.Address{
		Script: &types.Script{
			CodeHash: systemscript.GetCodeHash(network, systemscript.Secp256k1Blake160MultisigAll),
			HashType: types.HashTypeType,
			Args:     args,
		},
		Network: network,
	}.Encode()
	fmt.Println("multisig address:", sender)
	iterator, err := collector.NewLiveCellIteratorFromAddress(client, sender)
	if err != nil {
		t.Fatal(err)
	}
	receiver := "ckt1qpw9q60tppt7l3j7r09qcp7lxnp3vcanvgha8pmvsa3jplykxn32sqdunqvd3g2felqv6qer8pkydws8jg9qxlca0st5v"
	// build transaction
	builder := builder.NewCkbTransactionBuilder(network, iterator)
	builder.FeeRate = 1000
	if err := builder.AddOutputByAddress(receiver, 10355999291); err != nil {
		t.Fatal()
	}
	builder.AddChangeOutputByAddress(sender)
	txWithGroups, err := builder.Build(multisigConfig)
	if err != nil {
		t.Fatal(err)
	}

	// sign transaction
	txSigner := signer.GetTransactionSignerInstance(network)

	
	i := 1
	for index, pri := range priList {
		if index == 7 || index == 8 {
			continue
		}
		ctx1, _ := transaction.NewContextWithPayload(pri, multisigConfig)
		if _, err = txSigner.SignTransaction(txWithGroups, ctx1); err != nil {
			t.Fatal(err)
		}

		fmt.Println("private:", pri)
		if i == int(threshold) {
			break
		}
		i++
	}
	hash, err := client.SendTransaction(context.Background(), txWithGroups.TxView)
	if err != nil {
		t.Fatal(err)
	}
	fmt.Println("transaction hash: " + hexutil.Encode(hash.Bytes()))
}
@ekko-Huang ekko-Huang added the t:bug Type: This doesn't seem right. label Jan 19, 2025
@eval-exec
Copy link
Collaborator

		if index == 7 || index == 8 {
			continue
		}

Why do you skip 7 and 8 in priList?

@ekko-Huang
Copy link
Author

if index == 7 || index == 8 {
continue
}
Why do you skip 7 and 8 in priList?

In order to verify whether skipping checks 7 and 8 (skipping 1 and 5 and others will also fail) causes the validation failure, I am trying the verification rules of multisig transactions, because I haven't found the verification rules in the source code yet.

@eval-exec
Copy link
Collaborator

I am trying the verification rules of multisig transactions, because I haven't found the verification rules in the source code yet.

https://github.com/nervosnetwork/ckb-system-scripts/blob/b7472fbdbcd414dc8a4a74fb0a46a8bdc12158d4/c/secp256k1_blake160_multisig_all.c#L348-L367

@eval-exec
Copy link
Collaborator

eval-exec commented Jan 19, 2025

skipping 1 and 5 and others will also fail causes the validation failure

I tried skipping 1 and 4, it cause -52 validation failure too.

@eval-exec
Copy link
Collaborator

eval-exec commented Jan 19, 2025

I guess the the index == 10's priv key is wrong. Beacuse I tried below cases:

1. skipping 0,          success
2. skipping 0,1,        error -52
3. skipping 0,1,9,      error -52
4. skipping 0,1,10,     success
5. skipping 0,1,11,     error -52
6. skipping 0,1,2,10,   success
7. skipping 0,1,2,3,10, success

@eval-exec eval-exec added the s:waiting-on-author Status: The marked PR is awaiting some action (such as code changes) from the PR author. label Jan 19, 2025
@ekko-Huang
Copy link
Author

I guess the the index == 10's priv key is wrong. Beacuse I tried below cases:

All private keys should be correct, because the private keys for generating the multisig address and the private keys for signing are the same slice. When I have time, I will check the source code to find the verification rules. Currently, the verification rules are not yet clear.

@eval-exec eval-exec removed the s:waiting-on-author Status: The marked PR is awaiting some action (such as code changes) from the PR author. label Jan 19, 2025
@quake
Copy link
Member

quake commented Jan 19, 2025

The multisig contract deployed in the genesis block contains a bug that, under certain conditions, may fail to verify valid signatures. However, how to upgrade contracts deployed in the genesis block via a hard forrk is not converged on consensus yet. In PR 98, we addressed the issue and deployed a new multisig contract. As a workaround, we recommend transitioning to this new contract.The transaction construction and signing steps are same as the genesis deployed contract, the only difference is cell deps, please refer to this deployment info. I'm sorry for the inconvenience.

@ekko-Huang
Copy link
Author

The multisig contract deployed in the genesis block contains a bug that, under certain conditions, may fail to verify valid signatures. However, how to upgrade contracts deployed in the genesis block via a hard forrk is not converged on consensus yet. In PR 98, we addressed the issue and deployed a new multisig contract. As a workaround, we recommend transitioning to this new contract.The transaction construction and signing steps are same as the genesis deployed contract, the only difference is cell deps, please refer to this deployment info. I'm sorry for the inconvenience.

Thanks, then I should be able to resolve this by recreating the multisig address using the updated contract codeHash, is that right?

@quake
Copy link
Member

quake commented Jan 19, 2025

yes, please let us know if there is a problem.

@ekko-Huang
Copy link
Author

I'd like to know how I should replace this data.

testnetContracts[Secp256k1Blake160MultisigAll] = &Info{
		CodeHash: types.HexToHash("0x5c5069eb0857efc65e1bca0c07df34c31663b3622fd3876c876320fc9634e2a8"),
		HashType: types.HashTypeType,
		OutPoint: &types.OutPoint{
			TxHash: types.HexToHash("0xf8de3bb47d055cdf460d93a2a6e1b05f7432f9777c8c474abf4eec1d4aee5d37"),
			Index:  1,
		},
		DepType: types.DepTypeDepGroup,
	}

I see that the deployment information doesn't include code_hash.

deployed on testnet:

{
      "name": "multisig",
      "tx_hash": "0xe6774580c98c8b15799c628f539ed5722f3bc2b17206c2280e15f99be3c1ad71",
      "index": 0,
      "occupied_capacity": 5255000000000,
      "data_hash": "0x50c8623ef5112510ccdf2d8e480d02d0de7288eb9968f8b019817340c3991145",
      "type_id": "0x765b3ed6ae264b335d07e73ac332bf2c0f38f8d3340ed521cb447b4c42dd5f09"
}

@XuJiandong
Copy link
Contributor

XuJiandong commented Jan 20, 2025

code_hash: 0x765b3ed6ae264b335d07e73ac332bf2c0f38f8d3340ed521cb447b4c42dd5f09
hash_type: type

Also update the DepType to the other value: not group (which I don't know the exact name in Go).

Something like below:

testnetContracts[Secp256k1Blake160MultisigAll] = &Info{
		CodeHash: types.HexToHash("0x765b3ed6ae264b335d07e73ac332bf2c0f38f8d3340ed521cb447b4c42dd5f09"),
		HashType: types.HashTypeType,
		OutPoint: &types.OutPoint{
			TxHash: types.HexToHash("0xe6774580c98c8b15799c628f539ed5722f3bc2b17206c2280e15f99be3c1ad71"),
			Index:  0,
		},
		DepType: types.DepTypeCode???,
	}

@ekko-Huang
Copy link
Author

code_hash: 0x765b3ed6ae264b335d07e73ac332bf2c0f38f8d3340ed521cb447b4c42dd5f09
hash_type: type

Also update the DepType to the other value: not group (which I don't know the exact name in Go).

Something like below:

testnetContracts[Secp256k1Blake160MultisigAll] = &Info{
		CodeHash: types.HexToHash("0x765b3ed6ae264b335d07e73ac332bf2c0f38f8d3340ed521cb447b4c42dd5f09"),
		HashType: types.HashTypeType,
		OutPoint: &types.OutPoint{
			TxHash: types.HexToHash("0xe6774580c98c8b15799c628f539ed5722f3bc2b17206c2280e15f99be3c1ad71"),
			Index:  0,
		},
		DepType: types.DepTypeCode???,
	}

Thank you. I attempted to modify the data as you recommended. It seems DepType has two possible values, both of which I've tested. Changing DepType doesn't change the multisig address.

const (
    DepTypeCode     DepType = "code"
    DepTypeDepGroup DepType = "dep_group"
)

An error was returned when submitting the transaction:

TransactionFailedToVerify: Verification failed Script(TransactionScriptError { source: Inputs[0].Lock, cause: ScriptNotFound: code_hash: Byte32(0x765b3ed6ae264b335d07e73ac332bf2c0f38f8d3340ed521cb447b4c42dd5f09) })

@XuJiandong
Copy link
Contributor

It seems that you used mainnet config:
See nervosnetwork/ckb-system-scripts#98. Use this instead:

{
      "name": "multisig",
      "tx_hash": "0x0a13d8d9c83c3374196ee43d4f0116dac497b0fec3e71c04f7cb7780abc455d8",
      "index": 0,
      "occupied_capacity": 5255000000000,
      "data_hash": "0x50c8623ef5112510ccdf2d8e480d02d0de7288eb9968f8b019817340c3991145",
      "type_id": "0xd1a9f877aed3f5e07cb9c52b61ab96d06f250ae6883cc7f0a2423db0976fc821"
}

on testnet.

@ekko-Huang
Copy link
Author

I used the testnet configuration, and when cellDepType was code, the following error was returned:

TransactionFailedToVerify: Verification failed Script(TransactionScriptError { source: Inputs[0].Lock, cause: ValidationFailure: see error code -101 on page https://nervosnetwork.github.io/ckb-script-error-codes/by-type-hash/765b3ed6ae264b335d07e73ac332bf2c0f38f8d3340ed521cb447b4c42dd5f09.html#-101 })

When cellDepType was dep_group, the following error was returned:

TransactionFailedToResolve: Resolve failed InvalidDepGroup(OutPoint(0xe6774580c98c8b15799c628f539ed5722f3bc2b17206c2280e15f99be3c1ad7100000000))

So far, I'm still not sure what the problem is. Are there any changelog notes or available SDK examples?

transaction:

{
	"version": "0x0",
	"cell_deps": [{
		"out_point": {
			"tx_hash": "0xe6774580c98c8b15799c628f539ed5722f3bc2b17206c2280e15f99be3c1ad71",
			"index": "0x0"
		},
		"dep_type": "code"
	}],
	"header_deps": [],
	"inputs": [{
		"previous_output": {
			"tx_hash": "0x23129a1f636a636ade7831b55a34d37dd54b81f38f1e162afb2faab8047e2931",
			"index": "0x0"
		},
		"since": "0x0"
	}],
	"outputs": [{
		"lock": {
			"code_hash": "0x5c5069eb0857efc65e1bca0c07df34c31663b3622fd3876c876320fc9634e2a8",
			"hash_type": "type",
			"args": "0xbc9818d8a149cfc0cd0323386c46ba07920a037f"
		},
		"type": null,
		"capacity": "0x26944023b"
	}, {
		"lock": {
			"code_hash": "0x765b3ed6ae264b335d07e73ac332bf2c0f38f8d3340ed521cb447b4c42dd5f09",
			"hash_type": "type",
			"args": "0x9b0714519902dbd3ebf065244db01fdb7cc49499"
		},
		"type": null,
		"capacity": "0xe66b6108bd"
	}],
	"outputs_data": ["0x", "0x"],
	"witnesses": ["ignore......"]
}

@XuJiandong
Copy link
Contributor

You should also include previous dep_group. So the final tx should be like below:

	"cell_deps": [{
		"out_point": {
			"tx_hash": "0xf8de3bb47d055cdf460d93a2a6e1b05f7432f9777c8c474abf4eec1d4aee5d37",
			"index": "0x1"
		},
		"dep_type": "dep_group"

		"out_point": {
			"tx_hash": "0xe6774580c98c8b15799c628f539ed5722f3bc2b17206c2280e15f99be3c1ad71",
			"index": "0x0"
		},
		"dep_type": "code"
	}],

The dep_group one includes the secp256k1 precomputed table which will be used by multisig.

@ekko-Huang
Copy link
Author

It's successful. Thank you very much. This issue can be closed now

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
t:bug Type: This doesn't seem right.
Projects
None yet
Development

No branches or pull requests

4 participants