I am working with the Go SDK. In my use case I have signed payment transaction received from the network (i.e. msgpack). After decoding the msgpack data into the Types.SignedTxn object is it possible to verify the transaction or signature are valid and correct without broadcasting the transaction (e.g. SendRawTransaction)? Specifically I want to save on submitting the transaction if it is obviously invalid, tampered with or incorrect.
// txidPrefix is prepended to a transaction when computing its txid
var txidPrefix = []byte("TX")
// RawTransactionBytesToSign returns the byte form of the tx that we actually sign
// and compute txID from.
func RawTransactionBytesToSign(tx types.Transaction) []byte {
// Encode the transaction as msgpack
encodedTx := msgpack.Encode(tx)
// Prepend the hashable prefix
msgParts := [][]byte{txidPrefix, encodedTx}
return bytes.Join(msgParts, nil)
}
// VerifySignature verifies a simple signature (not a multisig or logicsig)
func VerifySignature(tx types.Transaction, pk ed25519.PublicKey, sig types.Signature) bool {
toBeSigned := RawTransactionBytesToSign(tx)
return ed25519.Verify(pk, toBeSigned, sig[:])
}
// AddrToED25519PublicKey copies an address to pk
func AddrToED25519PublicKey(a types.Address) (pk ed25519.PublicKey) {
pk = make([]byte, len(a))
copy(pk, a[:])
return
}
// CheckSignature verifies that stx is either a single signature
func CheckSignature(stx types.SignedTxn) error {
if (stx.Sig != types.Signature{}) { // contains a regular signature
// Single signature
// Check msig is empty
if len(stx.Msig.Subsigs) != 0 || stx.Msig.Version != 0 || stx.Msig.Threshold != 0 {
return errors.New("tx has both a sig and msig")
}
// Check lsig is empty
if !stx.Lsig.Blank() {
return errors.New("tx has both a sig and lsig")
}
pk := AddrToED25519PublicKey(stx.Txn.Sender)
if (stx.Sig == types.Signature{}) && !requireSigned {
return nil
}
if !VerifySignature(stx.Txn, pk, stx.Sig) {
return errors.New("signature does not verify")
}
}
return errors.New("msig/lsig not supported")
}
Excellent. That worked. Thank you!
Edit: Removed my suggested changed that was here; the “msig/lsig not supported” should be returned in the case it’s not stx.Sig. However, I do need a return nil
just above this line after the signature verification check has passed.
Hoping maybe someone converted this to the Javascript SDK and can share?
Also, for reference, this is what we are trying to achieve.
I was looking for the Python way of verifying the signature of a SignedTransaction but did not found so I wrote it myself
Here it is :
import base64
import algosdk
from nacl.signing import VerifyKey
def verify_signature(signed_tx: algosdk.future.transaction.SignedTransaction):
txn = algosdk.encoding.msgpack_encode(signed_tx.transaction)
message_to_sign = algosdk.constants.txid_prefix + base64.b64decode(txn)
public_key = algosdk.encoding.decode_address(signed_tx.transaction.sender)
signature = base64.b64decode(signed_tx.signature)
verify_key = VerifyKey(public_key)
verify_key.verify(message_to_sign, signature)
Maybe this should be added to the Python SDK in the SignedTransaction class.
This is useful when reconstructing a SignedTransaction object from a base64 provided by the front-end (not trustable) before storing the signed transaction in a database for example.