I was able to do a partial count, but I'm having a hard time counting it for P2SH and SegWit transactions.
Parse the block and store result:
bitcoin-cli getblock 00000000000000000002ec935e245f8ae70fc68cc828f05bf4cfa002668599e4 2 > block.json
This lists transactions like so:
{
"txid": "914b24cf50f4bc930c3a11123411031010f010563ed293f65f1ef8cb9bc1e950",
...
"vin": [
{
"txid": "cf3349976fedfe34502333310f753d91c16dccb3220d37d5f4c1a11b70eceda2",
"vout": 1,
"scriptSig": {
"asm": "30440220253e9a3fc9cbae7833abc38ea29ae4f7672cb33591c227446c71b26a8199760a022071042cde824b3cfddc51888d49ac8f85062921e1430ee85885252e1a6758fc17[ALL] 02b704c5bac9ed2e23193f28f0a9e1108ff6164e713552fca936c9447566df1d66",
"hex": "..."
},
"sequence": 4294967295
}
],
"vout": [
{
"value": 0.00071520,
"n": 1,
"scriptPubKey": {
"asm": "OP_DUP OP_HASH160 8342c5f824ae0806236e746b951e68192c1b124f OP_EQUALVERIFY OP_CHECKSIG",
"address": "1Cy3Wd2pArq359z9d3VYMUoBQBM1kkANCn",
"type": "pubkeyhash"
}
}
]
I can use jq, grep, sed, awkandwc -l` to count various stuff.
Legacy OP_CHECKSIG:
cat block.json | jq -r '.tx[].vout[].scriptPubKey.asm' | grep OP_CHECKSIG | wc -l
830
Each OP_CHECKSIG used to count as 1 sigop before SegWit, and now counts as 4.
This yields 830 * 4 = 3,320 sigops
Bare multisig:
cat block.json | jq -r '.tx[].vout[].scriptPubKey.asm' | grep OP_CHECKMULTISIG | wc -l
890
Each OP_CHECKMULTISIG counted as 20 sigops before SegWit, regardless of the number of public keys.
This yields 890 * 20 * 4 = 71,200 sigops.
P2SH:
Now it gets a bit more complicated. Because the P2SH script is only revealed when you spend it, we have to look at scriptSig on the inputs. Specifically we're looking for a scriptSig that's not one of these:
- A regular
OP_CHECKSIG spend, which always starts with a DER encoded signature (^304.*\[.*) and ends with a pubkey (0.*$).
- P2SH wrapped v0 SegWit:
^0014.*
We also assume it contains at least one signature, which we recognise by the presence of a sighash marker like [ALL]. The script itself is the last thing pushed on the stack, which is what the awk command gives us. We then pipe that into decodescript.
cat block.json | jq -r '.tx[].vin[].scriptSig.asm'| grep . | grep -v "^304.*\[.* 0.*$" | grep -v "^0014.*" | grep "\[" | awk 'NF>1{print $NF}' | xargs -i -n 1 bitcoin-cli decodescript {} | jq '.asm'
The result is a very short list consisting of 13 2-of-3 multisig transactions and 1 OP_CHECKSIG.
So that yields (13 * 3 + 1) * 4 = 160 sigops
Pay to witness public key hash:
For this we look at the txinwitness field. Specifically we check if the last stack entry is a public key.
cat block.json | jq -r '.tx[].vin[].txinwitness[-1]' | grep . | grep -v null | grep "^0..*" | wc -l
3981
This yields 3,981 sigops. Update: Vojtěch found 2219 + 1751 = 3,970 in his answer
Pay to witness script hash:
We again look at the last item on the witness stack, which we assume to be the script. We do the opposite of P2WPK and drop all matches with a public key. We also drop (taproot) Schnorr signatures which are 64 bytes plus optionally 1 for the SIGHASH. We also drop entries that start with 33 bytes (I think these are Tapleaf script hashes).
cat block.json | jq -r '.tx[].vin[].txinwitness[-1]' | grep . | grep -v null | grep -v "^0..*" | sed -r '/^.{128,130}$/d' | sed -r '/^.{66}$/d' | xargs -i -n 1 bitcoin-cli decodescript {} | jq '.asm' | grep -o "OP_CHECKSIG" | wc -l
231
This yields 231 sigops.
We count multisig in a similar way. First we sort them by N and not which N's are used:
| jq -r '.tx[].vin[].txinwitness[-1]' | grep . | grep -v null | grep -v "^03.*" | sed -r '/^.{128,130}$/d' | sed -r '/^.{66}$/d' | xargs -i -n 1 bitcoin-cli decodescript {} | jq '.asm' | grep "OP_CHECKMULTISIG" | rev | sort | rev
Then count the number of occurrences:
- 2 times m-of-7: 2 * 7 = 14 sigops
- 1 time m-of-4: 4 sigops
- 102 times m-of-3: 306 sigops
- 385 times m-of-2: 770 sigops
- List item
- 27 times 1-of-1: 27 sigops
So that yields 1,159 sigops.
Total
3320 + 71200 + 160 + 3981 + 231 + 1159 = 80,051