Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions cmd/goal/clerk.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"os"

"github.com/algorand/go-algorand/crypto"
"github.com/algorand/go-algorand/data/basics"
"github.com/algorand/go-algorand/data/transactions"
"github.com/algorand/go-algorand/protocol"

Expand Down Expand Up @@ -61,8 +62,8 @@ func init() {
sendCmd.Flags().StringVarP(&toAddress, "to", "t", "", "Address to send to money to (required)")
sendCmd.Flags().Uint64VarP(&amount, "amount", "a", 0, "The amount to be transferred (required), in microAlgos")
sendCmd.Flags().Uint64Var(&fee, "fee", 0, "The transaction fee (automatically determined by default), in microAlgos")
sendCmd.Flags().Uint64Var(&firstValid, "firstvalid", 0, "The first round where the transaction may be committed to the ledger (currently ignored)")
sendCmd.Flags().Uint64Var(&lastValid, "lastvalid", 0, "The last round where the transaction may be committed to the ledger (currently ignored)")
sendCmd.Flags().Uint64Var(&firstValid, "firstvalid", 0, "The first round where the transaction may be committed to the ledger")
sendCmd.Flags().Uint64Var(&lastValid, "lastvalid", 0, "The last round where the transaction may be committed to the ledger")
sendCmd.Flags().StringVar(&noteBase64, "noteb64", "", "Note (URL-base64 encoded)")
sendCmd.Flags().StringVarP(&noteText, "note", "n", "", "Note text (ignored if --noteb64 used also)")
sendCmd.Flags().StringVarP(&txFilename, "out", "o", "", "Dump an unsigned tx to the given file. In order to dump a signed transaction, pass -s")
Expand Down Expand Up @@ -145,7 +146,7 @@ var sendCmd = &cobra.Command{
if txFilename == "" {
// Sign and broadcast the tx
wh, pw := ensureWalletHandleMaybePassword(dataDir, walletName, true)
tx, err := client.SendPaymentFromWallet(wh, pw, fromAddressResolved, toAddressResolved, fee, amount, noteBytes, closeToAddressResolved)
tx, err := client.SendPaymentFromWallet(wh, pw, fromAddressResolved, toAddressResolved, fee, amount, noteBytes, closeToAddressResolved, basics.Round(firstValid), basics.Round(lastValid))

// update information from Transaction
txid := tx.ID().String()
Expand Down Expand Up @@ -191,7 +192,7 @@ var sendCmd = &cobra.Command{
}
}
} else {
payment, err := client.ConstructPayment(fromAddressResolved, toAddressResolved, fee, amount, noteBytes, closeToAddressResolved)
payment, err := client.ConstructPayment(fromAddressResolved, toAddressResolved, fee, amount, noteBytes, closeToAddressResolved, basics.Round(firstValid), basics.Round(lastValid))
if err != nil {
reportErrorf(errorConstructingTX, err)
}
Expand Down
28 changes: 22 additions & 6 deletions libgoal/libgoal.go
Original file line number Diff line number Diff line change
Expand Up @@ -428,13 +428,17 @@ type MultisigInfo struct {
}

// SendPaymentFromWallet signs a transaction using the given wallet and returns the resulted transaction id
func (c *Client) SendPaymentFromWallet(walletHandle, pw []byte, from, to string, fee, amount uint64, note []byte, closeTo string) (transactions.Transaction, error) {
func (c *Client) SendPaymentFromWallet(walletHandle, pw []byte, from, to string, fee, amount uint64, note []byte, closeTo string, firstValid, lastValid basics.Round) (transactions.Transaction, error) {
// Build the transaction
tx, err := c.ConstructPayment(from, to, fee, amount, note, closeTo)
tx, err := c.ConstructPayment(from, to, fee, amount, note, closeTo, firstValid, lastValid)
if err != nil {
return transactions.Transaction{}, err
}

return c.signAndBroadcastTransactionWithWallet(walletHandle, pw, tx)
}

func (c *Client) signAndBroadcastTransactionWithWallet(walletHandle, pw []byte, tx transactions.Transaction) (transactions.Transaction, error) {
// Sign the transaction
kmd, err := c.ensureKmdClient()
if err != nil {
Expand Down Expand Up @@ -467,7 +471,9 @@ func (c *Client) SendPaymentFromWallet(walletHandle, pw []byte, from, to string,

// ConstructPayment builds a payment transaction to be signed
// If the fee is 0, the function will use the suggested one form the network
func (c *Client) ConstructPayment(from, to string, fee, amount uint64, note []byte, closeTo string) (transactions.Transaction, error) {
// if the lastValid is 0, firstValid + maxTxnLifetime will be used
// if the firstValid is 0, lastRound + 1 will be used
func (c *Client) ConstructPayment(from, to string, fee, amount uint64, note []byte, closeTo string, firstValid, lastValid basics.Round) (transactions.Transaction, error) {
fromAddr, err := basics.UnmarshalChecksumAddress(from)
if err != nil {
return transactions.Transaction{}, err
Expand All @@ -484,15 +490,25 @@ func (c *Client) ConstructPayment(from, to string, fee, amount uint64, note []by
return transactions.Transaction{}, err
}

round := params.LastRound
cp := config.Consensus[protocol.ConsensusVersion(params.ConsensusVersion)]
if firstValid == 0 && lastValid == 0 {
firstValid = basics.Round(params.LastRound + 1)
lastValid = firstValid + basics.Round(cp.MaxTxnLife)
} else if firstValid != 0 && lastValid == 0 {
lastValid = firstValid + basics.Round(cp.MaxTxnLife)
} else if firstValid > lastValid {
return transactions.Transaction{}, fmt.Errorf("cannot construct payment: txn would first be valid on round %d which is after last valid round %d", firstValid, lastValid)
} else if lastValid-firstValid > basics.Round(cp.MaxTxnLife) {
return transactions.Transaction{}, fmt.Errorf("cannot construct payment: txn validity period ( %d to %d ) is greater than protocol max txn lifetime %d ", firstValid, lastValid, cp.MaxTxnLife)
}

tx := transactions.Transaction{
Type: protocol.PaymentTx,
Header: transactions.Header{
Sender: fromAddr,
Fee: basics.MicroAlgos{Raw: fee},
FirstValid: basics.Round(round),
LastValid: basics.Round(round) + basics.Round(cp.MaxTxnLife),
FirstValid: firstValid,
LastValid: lastValid,
Note: note,
},
PaymentTxnFields: transactions.PaymentTxnFields{
Expand Down
2 changes: 1 addition & 1 deletion libgoal/unencryptedWallet.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ func (c *Client) SendPaymentFromUnencryptedWallet(from, to string, fee, amount u
return transactions.Transaction{}, err
}

return c.SendPaymentFromWallet(wh, nil, from, to, fee, amount, note, "")
return c.SendPaymentFromWallet(wh, nil, from, to, fee, amount, note, "", 0, 0)
}

// GetUnencryptedWalletHandle returns the unencrypted wallet handle. If there
Expand Down
4 changes: 2 additions & 2 deletions test/e2e-go/cli/perf/payment_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ func BenchmarkSendPayment(b *testing.B) {
for i := 0; i < b.N; i++ {
var nonce [8]byte
crypto.RandBytes(nonce[:])
tx, err = c.ConstructPayment(addr, addr, 1, 1, nonce[:], "")
tx, err = c.ConstructPayment(addr, addr, 1, 1, nonce[:], "", 0, 0)
require.NoError(b, err)
}
})
Expand All @@ -74,7 +74,7 @@ func BenchmarkSendPayment(b *testing.B) {
for i := 0; i < b.N; i++ {
var nonce [8]byte
crypto.RandBytes(nonce[:])
_, err := c.SendPaymentFromWallet(wallet, nil, addr, addr, 1, 1, nonce[:], "")
_, err := c.SendPaymentFromWallet(wallet, nil, addr, addr, 1, 1, nonce[:], "", 0, 0)
require.NoError(b, err)
}
})
Expand Down
6 changes: 3 additions & 3 deletions test/e2e-go/features/multisig/multisig_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ func TestBasicMultisig(t *testing.T) {
fixture.SendMoneyAndWait(curStatus.LastRound, amountToFund, minTxnFee, fundingAddr, multisigAddr)
// try to transact with 1 of 3
amountToSend := minAcctBalance
unsignedTransaction, err := client.ConstructPayment(multisigAddr, addrs[0], minTxnFee, amountToSend, nil, "")
unsignedTransaction, err := client.ConstructPayment(multisigAddr, addrs[0], minTxnFee, amountToSend, nil, "", 0, 0)
r.NoError(err, "Unexpected error when constructing payment transaction")
emptyPartial := crypto.MultisigSig{}
emptySignature := crypto.Signature{}
Expand All @@ -90,7 +90,7 @@ func TestBasicMultisig(t *testing.T) {
r.True(fixture.WaitForTxnConfirmation(curStatus.LastRound+uint64(5), multisigAddr, txid))

// Need a new txid to avoid dup detection
unsignedTransaction, err = client.ConstructPayment(multisigAddr, addrs[0], minTxnFee, amountToSend, []byte("foobar"), "")
unsignedTransaction, err = client.ConstructPayment(multisigAddr, addrs[0], minTxnFee, amountToSend, []byte("foobar"), "", 0, 0)
r.NoError(err, "Unexpected error when constructing payment transaction")
signatureWithOne, err = client.UnencryptedMultisigSignTransaction(unsignedTransaction, addrs[0], emptyPartial)
r.NoError(err, "first signing returned error")
Expand Down Expand Up @@ -196,7 +196,7 @@ func TestDuplicateKeys(t *testing.T) {
fixture.SendMoneyAndWait(curStatus.LastRound, amountToFund, txnFee, fundingAddr, multisigAddr)
// try to transact with "1" signature (though, this is a signature from "every" member of the multisig)
amountToSend := minAcctBalance
unsignedTransaction, err := client.ConstructPayment(multisigAddr, addrs[0], txnFee, amountToSend, nil, "")
unsignedTransaction, err := client.ConstructPayment(multisigAddr, addrs[0], txnFee, amountToSend, nil, "", 0, 0)
r.NoError(err, "Unexpected error when constructing payment transaction")
emptyPartial := crypto.MultisigSig{}
emptySignature := crypto.Signature{}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ func TestParticipationKeyOnlyAccountParticipatesCorrectly(t *testing.T) {
amountToSend := uint64(10000) // arbitrary
wh, err := client.GetUnencryptedWalletHandle()
a.NoError(err, "should get unencrypted wallet handle")
_, err = client.SendPaymentFromWallet(wh, nil, partkeyOnlyAccount, richAccount, amountToSend, transactionFee, nil, "")
_, err = client.SendPaymentFromWallet(wh, nil, partkeyOnlyAccount, richAccount, amountToSend, transactionFee, nil, "", basics.Round(0), basics.Round(0))
a.Error(err, "attempt to send money from partkey-only account should be treated as though wallet is not controlled")
// partkeyonly_account attempts to go offline, should fail (no rootkey to sign txn with)
goOfflineUTx, err := client.MakeUnsignedGoOfflineTx(partkeyOnlyAccount, 0, 0, transactionFee)
Expand Down
2 changes: 1 addition & 1 deletion test/e2e-go/features/transactions/close_account_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ func TestAccountsCanClose(t *testing.T) {
a.NoError(err)
fixture.WaitForConfirmedTxn(status.LastRound+10, baseAcct, tx.ID().String())

tx, err = client.SendPaymentFromWallet(walletHandle, nil, acct0, acct1, 1, 100000, nil, acct2)
tx, err = client.SendPaymentFromWallet(walletHandle, nil, acct0, acct1, 1, 100000, nil, acct2, 0, 0)
a.NoError(err)
fixture.WaitForConfirmedTxn(status.LastRound+10, acct0, tx.ID().String())

Expand Down
34 changes: 17 additions & 17 deletions test/e2e-go/restAPI/restClient_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ func TestTransactionsByAddr(t *testing.T) {
t.Error("no addr with funds")
}
toAddress := getDestAddr(t, testClient, addresses, someAddress, wh)
tx, err := testClient.SendPaymentFromWallet(wh, nil, someAddress, toAddress, 10000, 100000, nil, "")
tx, err := testClient.SendPaymentFromWallet(wh, nil, someAddress, toAddress, 10000, 100000, nil, "", 0, 0)
require.NoError(t, err)
txID := tx.ID()
rnd, err := testClient.Status()
Expand Down Expand Up @@ -256,7 +256,7 @@ func TestClientRejectsBadFromAddressWhenSending(t *testing.T) {
require.NoError(t, err)
badAccountAddress := "This is absolutely not a valid account address."
goodAccountAddress := addresses[0]
_, err = testClient.SendPaymentFromWallet(wh, nil, badAccountAddress, goodAccountAddress, 10000, 100000, nil, "")
_, err = testClient.SendPaymentFromWallet(wh, nil, badAccountAddress, goodAccountAddress, 10000, 100000, nil, "", 0, 0)
require.Error(t, err)
}

Expand All @@ -269,7 +269,7 @@ func TestClientRejectsBadToAddressWhenSending(t *testing.T) {
require.NoError(t, err)
badAccountAddress := "This is absolutely not a valid account address."
goodAccountAddress := addresses[0]
_, err = testClient.SendPaymentFromWallet(wh, nil, goodAccountAddress, badAccountAddress, 10000, 100000, nil, "")
_, err = testClient.SendPaymentFromWallet(wh, nil, goodAccountAddress, badAccountAddress, 10000, 100000, nil, "", 0, 0)
require.Error(t, err)
}

Expand All @@ -289,7 +289,7 @@ func TestClientRejectsMutatedFromAddressWhenSending(t *testing.T) {
require.NoError(t, err)
}
mutatedAccountAddress := mutateStringAtIndex(unmutatedAccountAddress, 0)
_, err = testClient.SendPaymentFromWallet(wh, nil, mutatedAccountAddress, goodAccountAddress, 10000, 100000, nil, "")
_, err = testClient.SendPaymentFromWallet(wh, nil, mutatedAccountAddress, goodAccountAddress, 10000, 100000, nil, "", 0, 0)
require.Error(t, err)
}

Expand All @@ -309,7 +309,7 @@ func TestClientRejectsMutatedToAddressWhenSending(t *testing.T) {
require.NoError(t, err)
}
mutatedAccountAddress := mutateStringAtIndex(unmutatedAccountAddress, 0)
_, err = testClient.SendPaymentFromWallet(wh, nil, goodAccountAddress, mutatedAccountAddress, 10000, 100000, nil, "")
_, err = testClient.SendPaymentFromWallet(wh, nil, goodAccountAddress, mutatedAccountAddress, 10000, 100000, nil, "", 0, 0)
require.Error(t, err)
}

Expand All @@ -322,7 +322,7 @@ func TestClientRejectsSendingMoneyFromAccountForWhichItHasNoKey(t *testing.T) {
require.NoError(t, err)
goodAccountAddress := addresses[0]
nodeDoesNotHaveKeyForThisAddress := "NJY27OQ2ZXK6OWBN44LE4K43TA2AV3DPILPYTHAJAMKIVZDWTEJKZJKO4A"
_, err = testClient.SendPaymentFromWallet(wh, nil, nodeDoesNotHaveKeyForThisAddress, goodAccountAddress, 10000, 100000, nil, "")
_, err = testClient.SendPaymentFromWallet(wh, nil, nodeDoesNotHaveKeyForThisAddress, goodAccountAddress, 10000, 100000, nil, "", 0, 0)
require.Error(t, err)
}

Expand All @@ -344,7 +344,7 @@ func TestClientOversizedNote(t *testing.T) {
}
maxTxnNoteBytes := config.Consensus[protocol.ConsensusCurrentVersion].MaxTxnNoteBytes
note := make([]byte, maxTxnNoteBytes+1)
_, err = testClient.SendPaymentFromWallet(wh, nil, fromAddress, toAddress, 10000, 100000, note, "")
_, err = testClient.SendPaymentFromWallet(wh, nil, fromAddress, toAddress, 10000, 100000, note, "", 0, 0)
require.Error(t, err)
}

Expand All @@ -363,7 +363,7 @@ func TestClientCanSendAndGetNote(t *testing.T) {
toAddress := getDestAddr(t, testClient, addresses, someAddress, wh)
maxTxnNoteBytes := config.Consensus[protocol.ConsensusCurrentVersion].MaxTxnNoteBytes
note := make([]byte, maxTxnNoteBytes)
tx, err := testClient.SendPaymentFromWallet(wh, nil, someAddress, toAddress, 10000, 100000, note, "")
tx, err := testClient.SendPaymentFromWallet(wh, nil, someAddress, toAddress, 10000, 100000, note, "", 0, 0)
require.NoError(t, err)
txStatus, err := waitForTransaction(t, testClient, someAddress, tx.ID().String(), 15*time.Second)
require.NoError(t, err)
Expand All @@ -383,7 +383,7 @@ func TestClientCanGetTransactionStatus(t *testing.T) {
t.Error("no addr with funds")
}
toAddress := getDestAddr(t, testClient, addresses, someAddress, wh)
tx, err := testClient.SendPaymentFromWallet(wh, nil, someAddress, toAddress, 10000, 100000, nil, "")
tx, err := testClient.SendPaymentFromWallet(wh, nil, someAddress, toAddress, 10000, 100000, nil, "", 0, 0)
t.Log(string(protocol.EncodeJSON(tx)))
require.NoError(t, err)
t.Log(tx.ID().String())
Expand Down Expand Up @@ -430,22 +430,22 @@ func TestSendingTooMuchFails(t *testing.T) {
fromBalance, err := testClient.GetBalance(fromAddress)
require.NoError(t, err)
// too much amount
_, err = testClient.SendPaymentFromWallet(wh, nil, fromAddress, toAddress, 10000, fromBalance+100, nil, "")
_, err = testClient.SendPaymentFromWallet(wh, nil, fromAddress, toAddress, 10000, fromBalance+100, nil, "", 0, 0)
t.Log(err)
require.Error(t, err)

// waaaay too much amount
_, err = testClient.SendPaymentFromWallet(wh, nil, fromAddress, toAddress, 10000, math.MaxUint64, nil, "")
_, err = testClient.SendPaymentFromWallet(wh, nil, fromAddress, toAddress, 10000, math.MaxUint64, nil, "", 0, 0)
t.Log(err)
require.Error(t, err)

// too much fee
_, err = testClient.SendPaymentFromWallet(wh, nil, fromAddress, toAddress, fromBalance+100, 10000, nil, "")
_, err = testClient.SendPaymentFromWallet(wh, nil, fromAddress, toAddress, fromBalance+100, 10000, nil, "", 0, 0)
t.Log(err)
require.Error(t, err)

// waaaay too much fee
_, err = testClient.SendPaymentFromWallet(wh, nil, fromAddress, toAddress, math.MaxUint64, 10000, nil, "")
_, err = testClient.SendPaymentFromWallet(wh, nil, fromAddress, toAddress, math.MaxUint64, 10000, nil, "", 0, 0)
t.Log(err)
require.Error(t, err)
}
Expand Down Expand Up @@ -482,7 +482,7 @@ func TestSendingFromEmptyAccountFails(t *testing.T) {
toAddress, err = testClient.GenerateAddress(wh)
require.NoError(t, err)
}
_, err = testClient.SendPaymentFromWallet(wh, nil, fromAddress, toAddress, 10000, 100000, nil, "")
_, err = testClient.SendPaymentFromWallet(wh, nil, fromAddress, toAddress, 10000, 100000, nil, "", 0, 0)
require.Error(t, err)
}

Expand Down Expand Up @@ -511,7 +511,7 @@ func TestSendingTooLittleToEmptyAccountFails(t *testing.T) {
if someAddress == "" {
t.Error("no addr with funds")
}
_, err = testClient.SendPaymentFromWallet(wh, nil, someAddress, emptyAddress, 10000, 1, nil, "")
_, err = testClient.SendPaymentFromWallet(wh, nil, someAddress, emptyAddress, 10000, 1, nil, "", 0, 0)
require.Error(t, err)
}

Expand All @@ -531,7 +531,7 @@ func TestSendingLowFeeFails(t *testing.T) {
t.Errorf("balance too low %d < %d", someBal, sendAmount)
}
toAddress := getDestAddr(t, testClient, addresses, someAddress, wh)
utx, err := testClient.ConstructPayment(someAddress, toAddress, 1, sendAmount, nil, "")
utx, err := testClient.ConstructPayment(someAddress, toAddress, 1, sendAmount, nil, "", 0, 0)
require.NoError(t, err)
utx.Fee.Raw = 1
stx, err := testClient.SignTransactionWithWallet(wh, nil, utx)
Expand Down Expand Up @@ -586,7 +586,7 @@ func TestSendingNotClosingAccountFails(t *testing.T) {
t.Error("no addr with funds")
}
amt := someBal - 10000 - 1
_, err = testClient.SendPaymentFromWallet(wh, nil, someAddress, emptyAddress, 10000, amt, nil, "")
_, err = testClient.SendPaymentFromWallet(wh, nil, someAddress, emptyAddress, 10000, amt, nil, "", 0, 0)
require.Error(t, err)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func cascadeCreateAndFundAccounts(amountToSend, transactionFee uint64, fundingAc
a.NoError(err, "should be able to get unencrypted wallet handle")
newAddress, err := client.GenerateAddress(wh)
a.NoError(err, "should be able to generate new address")
tx, err := client.SendPaymentFromWallet(wh, nil, fundingAccount, newAddress, transactionFee, amountToSend, nil, "")
tx, err := client.SendPaymentFromWallet(wh, nil, fundingAccount, newAddress, transactionFee, amountToSend, nil, "", 0, 0)
a.NoError(err, "should be no errors when funding new accounts, send number %v", i)
i++
outputTxidsToAccounts[tx.ID().String()] = newAddress
Expand Down
2 changes: 1 addition & 1 deletion test/framework/fixtures/restClientFixture.go
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ func (f *RestClientFixture) SendMoneyAndWait(curRound, amountToSend, transaction
// SendMoneyAndWaitFromWallet is as above, but for a specific wallet
func (f *RestClientFixture) SendMoneyAndWaitFromWallet(walletHandle, walletPassword []byte, curRound, amountToSend, transactionFee uint64, fromAccount, toAccount string) (fundingTxid string) {
client := f.LibGoalClient
fundingTx, err := client.SendPaymentFromWallet(walletHandle, walletPassword, fromAccount, toAccount, transactionFee, amountToSend, nil, "")
fundingTx, err := client.SendPaymentFromWallet(walletHandle, walletPassword, fromAccount, toAccount, transactionFee, amountToSend, nil, "", 0, 0)
require.NoError(f.t, err, "client should be able to send money from rich to poor account")
require.NotEmpty(f.t, fundingTx.ID().String(), "transaction ID should not be empty")
waitingDeadline := curRound + uint64(5)
Expand Down