hashgraph / hedera-sdk-go

Hedera™ Hashgraph SDK for Go
https://docs.hedera.com/docs/hedera-sdks
Apache License 2.0
108 stars 65 forks source link

INVALID_SIGNATURE when attempting to submit a transaction signed with an ECDSA private key #1063

Closed jbaldwinroberts closed 1 month ago

jbaldwinroberts commented 1 month ago

Description

I expected the code below to work given the documentation here: https://hips.hedera.com/hip/hip-222

Steps to reproduce

Execute this:

func Test_Debug(t *testing.T) {
    client := hedera.ClientForTestnet()

    // Set up the origin account and private key
    originAccountID, err := hedera.AccountIDFromString("0.0.4655904")
    require.NoError(t, err)

    b, err := hex.DecodeString("5dbe76cf36dd5e26c2f1a1b8b6d9ca4b24e7567ed6b4aa2f640461a6cd98616a")
    originPK, err := crypto.ToECDSA(b)
    require.NoError(t, err)

    // Set up the destination account
    destAccountID, err := hedera.AccountIDFromString("0.0.4843571")
    require.NoError(t, err)

    // Set up the transaction
    transaction := hedera.NewTransferTransaction()
    transaction.AddHbarTransfer(originAccountID, hedera.NewHbar(-1))
    transaction.AddHbarTransfer(destAccountID, hedera.NewHbar(1))

    // Specify the node account ID
    _, err = hedera.TransactionSetNodeAccountIDs(transaction, []hedera.AccountID{{Account: 3}})
    require.NoError(t, err)

    // Specify the transaction ID
    _, err = hedera.TransactionSetTransactionID(transaction, hedera.TransactionIDGenerate(originAccountID))
    require.NoError(t, err)

    // Sign the transaction
    _, err = transaction.Freeze()
    require.NoError(t, err)

    digest, err := getDigest(transaction)
    require.NoError(t, err)

    signature, err := crypto.Sign(digest, originPK)
    require.NoError(t, err)

    // Add the signature to the transaction and execute it
    transaction = transaction.AddSignature(client.GetOperatorPublicKey(), signature)
    transactionResponse, err := transaction.Execute(client)
    require.NoError(t, err)

    transactionReceipt, err := transactionResponse.GetReceipt(client)
    require.NoError(t, err)

    fmt.Printf("crypto transfer status: %v\n", transactionReceipt.Status)
}

func getDigest(transaction *hedera.TransferTransaction) ([]byte, error) {
    signedTransaction := services.SignedTransaction{}
    if err := prototext.Unmarshal([]byte(transaction.String()), &signedTransaction); err != nil {
        return nil, err
    }

    // https://hips.hedera.com/hip/hip-222
    hash := sha3.NewLegacyKeccak256()
    hash.Write([]byte(hex.EncodeToString(signedTransaction.BodyBytes)))
    hashSum := hash.Sum(nil)

    return hashSum, nil
}

Actual result: exceptional precheck status INVALID_SIGNATURE received for transaction 0.0.4655904@1726050695.936559436 Expected result: transaction is submitted

Additional context

No response

Hedera network

testnet

Version

v2.44.0

Operating system

macOS

jbaldwinroberts commented 1 month ago

this is the correct way to generate the digest:

func getDigest(transaction *hedera.TransferTransaction) ([]byte, error) {
    var signedTransaction services.SignedTransaction
    if err := prototext.Unmarshal([]byte(transaction.String()), &signedTransaction); err != nil {
        return nil, err
    }

    hash := crypto.Keccak256Hash(signedTransaction.BodyBytes)

    return hash.Bytes(), nil
}

with this change it works as expected.