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

[Testnet] Can't successfully sign transaction(transfer flow tokens between accounts) #16

Open
Nightstep opened this issue Jul 16, 2023 · 0 comments

Comments

@Nightstep
Copy link

I have 2 accounts on Testnet, previously I was able to send Flow tokens between accounts using Flow CLI. Now trying to send transaction using this SDK, but transaction fails on validation with error:

[Error Code: 1009] error caused by: 1 error occurred:
	* transaction verification failed: [Error Code: 1006] invalid proposal key: public key 0 on account 31bbb88233df243d does not have a valid signature: [Error Code: 1009] invalid envelope key: public key 0 on account 31bbb88233df243d does not have a valid signature: signature is not valid

Info:
https://f.dnz.dev/d6ff9c65feb166505043c420843d5504523f7a784f9b151550933dda6bb169db

I'd be grateful If you could point where to dig, because I totally stuck now.

Here's my code in ViewController.swift file:

import UIKit
import Flow
import CryptoKit

class ViewController: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        
        flow.configure(chainID: .testnet)
        Task {
            do {
                let aliceAddress = Flow.Address(hex: "0x31bbb88233df243d")
                let bobAddress = Flow.Address(hex: "0x8ce1531666464325")
                
                // get Alice account info:
                let aliceAccount: Flow.Account = try await flow.getAccountAtLatestBlock(address: aliceAddress)
                print("Alice's Balance:\(Double(aliceAccount.balance) / 100000000)")

                let bobAccount: Flow.Account = try await flow.getAccountAtLatestBlock(address: bobAddress)
                print("Bob's Balance:\(Double(bobAccount.balance) / 100000000)\n")

                // Preparing Alice's transaction:
                let amountArg = Flow.Argument(type: .ufix64, value: .ufix64(120))
                let recipientArg = Flow.Argument(type: .address, value: .address(bobAddress))
                
                let alicePrivateKey = try P256.Signing.PrivateKey(rawRepresentation: "0xd08382ba70946ee82af61147e6f84c69932e9c69baaf0c7177731fa0ddc28477".hexValue)
                
                let signer = ECDSA_P256_Signer(
                    address: aliceAccount.address,
                    keyIndex: aliceAccount.keys.first!.index,
                    privateKey: alicePrivateKey
                )
                let txID = try await flow.sendTransaction(chainID: .testnet,
                                     signers: [signer],
                                     script: Transactions.tokenTransfer,
                                     agrument: [amountArg, recipientArg],
                                     authorizer: [aliceAddress],
                                     payerAddress: aliceAddress,
                                     proposerKey: Flow.TransactionProposalKey(
                                        address: aliceAddress,
                                        keyIndex: aliceAccount.keys.first!.index,
                                        sequenceNumber: aliceAccount.keys.first!.sequenceNumber
                                     ),
                                     blockID: nil)

                print("Transaction sent.\nTx ID: \(txID)")
                
                let transactionResult = try await flow.getTransactionResultById(id: txID)
                print("Transaction Details:\n\(transactionResult)")

                // Check balances for both accounts after transaction:
                let aliceUpdated: Flow.Account = try await flow.getAccountAtLatestBlock(address: aliceAddress)
                print("Alice Updated Balance:\(Double(aliceUpdated.balance) / 100000000)\n")

                let bobUpdated: Flow.Account = try await flow.getAccountAtLatestBlock(address: bobAddress)
                print("Bob's Updated Balance:\(Double(bobUpdated.balance) / 100000000)\n")

                print("End of sequence.")
                
            } catch let error {
                print("Something went wrong")
                print(error.localizedDescription)
            }
            
        }
    }
}

private extension String {
    /// Convert hex string to bytes
    var hexValue: [UInt8] {
        var startIndex = self.startIndex
        return (0 ..< count / 2).compactMap { _ in
            let endIndex = index(after: startIndex)
            defer { startIndex = index(after: endIndex) }
            return UInt8(self[startIndex ... endIndex], radix: 16)
        }
    }
}

There was no example of correct FlowSigner implementation, but I found one in TestCases and modified it a bit:

import CryptoKit
import Foundation
import Flow
import CryptoSwift

struct ECDSA_P256_Signer: FlowSigner {
    var address: Flow.Address
    var keyIndex: Int

    var privateKey: P256.Signing.PrivateKey

    init(address: Flow.Address, keyIndex: Int, privateKey: P256.Signing.PrivateKey) {
        self.address = address
        self.keyIndex = keyIndex
        self.privateKey = privateKey
    }

    func sign(transaction _: Flow.Transaction, signableData: Data) throws -> Data {
        do {
            let digest = SHA256.hash(data: signableData.sha3(.sha256))
            return try privateKey.signature(for: digest).rawRepresentation
        } catch {
            throw error
        }
    }
}

Transaction Cadence code:

import Foundation

public struct Transactions {
    
    private static let fungibleTokenContractAddr: String = "0x9a0766d93b6608b7"
    private static let flowTokenContractAddr: String = "0x7e60df042a9c0868"
    
    public static let tokenTransfer: String = """
        
    import FungibleToken from \(fungibleTokenContractAddr)
    import FlowToken from \(flowTokenContractAddr)

    transaction(amount: UFix64, to: Address) {

        // The Vault resource that holds the tokens that are being transferred
        let sentVault: @FungibleToken.Vault

        prepare(signer: AuthAccount) {

            // Get a reference to the signer's stored vault
            let vaultRef = signer.borrow<&FlowToken.Vault>(from: /storage/flowTokenVault)
                ?? panic("Could not borrow reference to the owner's Vault!")

            // Withdraw tokens from the signer's stored vault
            self.sentVault <- vaultRef.withdraw(amount: amount)
        }

        execute {

            // Get the recipient's public account object
            let recipient = getAccount(to)

            // Get a reference to the recipient's Receiver
            let receiverRef = recipient.getCapability(/public/flowTokenReceiver)
                .borrow<&FlowToken.Vault{FungibleToken.Receiver}>()
                ?? panic("Could not borrow receiver reference to the recipient's Vault")

            // Deposit the withdrawn tokens in the recipient's receiver
            receiverRef.deposit(from: <-self.sentVault)
        }
    }
    """
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant