Skip to content

Commit 24047f4

Browse files
gmaloufjannotti
andauthored
Txn: Based on consensus param 'EnforceAuthAddrSenderDiff', check that auth… (#6504)
Co-authored-by: John Jannotti <jannotti@gmail.com>
1 parent 8b32f14 commit 24047f4

File tree

5 files changed

+87
-2
lines changed

5 files changed

+87
-2
lines changed

cmd/goal/clerk.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,9 @@ func writeTxnToFile(client libgoal.Client, signTx bool, dataDir string, walletNa
274274
if err != nil {
275275
reportErrorf("Signer invalid (%s): %v", signerAddress, err)
276276
}
277+
if authAddr == tx.Sender {
278+
reportErrorf("AuthAddr cannot be the same as the transaction sender")
279+
}
277280
}
278281

279282
stxn, err := createSignedTransaction(client, signTx, dataDir, walletName, tx, authAddr)
@@ -449,6 +452,9 @@ var sendCmd = &cobra.Command{
449452
if err != nil {
450453
reportErrorf("Signer invalid (%s): %v", signerAddress, err)
451454
}
455+
if authAddr == payment.Sender {
456+
reportErrorf("AuthAddr cannot be the same as the transaction sender")
457+
}
452458
}
453459

454460
var stx transactions.SignedTxn
@@ -531,7 +537,7 @@ var sendCmd = &cobra.Command{
531537

532538
// Append the signer since it's a rekey txn
533539
if basics.Address(addr) == stx.Txn.Sender {
534-
reportWarnln(rekeySenderTargetSameError)
540+
reportErrorf("AuthAddr cannot be the same as the transaction sender")
535541
}
536542
stx.AuthAddr = basics.Address(addr)
537543
}
@@ -862,6 +868,9 @@ var signCmd = &cobra.Command{
862868
if lsig.Logic != nil {
863869
txn.Lsig = lsig
864870
if signerAddress != "" {
871+
if authAddr == txn.Txn.Sender {
872+
reportErrorf("AuthAddr cannot be the same as the transaction sender")
873+
}
865874
txn.AuthAddr = authAddr
866875
}
867876
}

config/consensus.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,9 @@ type ConsensusParams struct {
247247
// SupportRekeying indicates support for account rekeying (the RekeyTo and AuthAddr fields)
248248
SupportRekeying bool
249249

250+
// EnforceAuthAddrSenderDiff requires that AuthAddr must be empty or different from Sender
251+
EnforceAuthAddrSenderDiff bool
252+
250253
// application support
251254
Application bool
252255

@@ -1461,6 +1464,7 @@ func initConsensusProtocols() {
14611464

14621465
vFuture.AppSizeUpdates = true
14631466
vFuture.AllowZeroLocalAppRef = true
1467+
vFuture.EnforceAuthAddrSenderDiff = true
14641468

14651469
Consensus[protocol.ConsensusFuture] = vFuture
14661470

data/transactions/verify/txn.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ var errTxGroupInvalidFee = errors.New("txgroup fee requirement overflow")
8484
var errTxnSigHasNoSig = errors.New("signedtxn has no sig")
8585
var errTxnSigNotWellFormed = errors.New("signedtxn should only have one of Sig or Msig or LogicSig")
8686
var errRekeyingNotSupported = errors.New("nonempty AuthAddr but rekeying is not supported")
87+
var errAuthAddrEqualsSender = errors.New("AuthAddr must be different from Sender")
8788
var errUnknownSignature = errors.New("has one mystery sig. WAT?")
8889

8990
// TxGroupErrorReason is reason code for ErrTxGroupError
@@ -168,6 +169,10 @@ func txnBatchPrep(gi int, groupCtx *GroupContext, verifier crypto.BatchVerifier)
168169
return &TxGroupError{err: errRekeyingNotSupported, GroupIndex: gi, Reason: TxGroupErrorReasonGeneric}
169170
}
170171

172+
if groupCtx.consensusParams.EnforceAuthAddrSenderDiff && !s.AuthAddr.IsZero() && s.AuthAddr == s.Txn.Sender {
173+
return &TxGroupError{err: errAuthAddrEqualsSender, GroupIndex: gi, Reason: TxGroupErrorReasonGeneric}
174+
}
175+
171176
if err := s.Txn.WellFormed(groupCtx.specAddrs, groupCtx.consensusParams); err != nil {
172177
return &TxGroupError{err: err, GroupIndex: gi, Reason: TxGroupErrorReasonNotWellFormed}
173178
}

data/transactions/verify/txn_test.go

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1531,3 +1531,70 @@ func TestLogicSigMsigBothFlags(t *testing.T) {
15311531
err = verifyLogicSig()
15321532
require.ErrorContains(t, err, "LogicSig should only have one of Sig, Msig, or LMsig but has more than one")
15331533
}
1534+
1535+
func TestAuthAddrSenderDiff(t *testing.T) {
1536+
partitiontest.PartitionTest(t)
1537+
1538+
t.Run("v41-disabled", func(t *testing.T) { testAuthAddrSenderDiff(t, protocol.ConsensusV41, false) })
1539+
t.Run("future-enabled", func(t *testing.T) { testAuthAddrSenderDiff(t, protocol.ConsensusFuture, true) })
1540+
}
1541+
1542+
func testAuthAddrSenderDiff(t *testing.T, consensusVer protocol.ConsensusVersion, enforceEnabled bool) {
1543+
secrets, addrs, _ := generateAccounts(3)
1544+
sender := addrs[0]
1545+
otherAddr := addrs[1]
1546+
1547+
blockHeader := createDummyBlockHeader(consensusVer)
1548+
proto := config.Consensus[consensusVer]
1549+
1550+
makeTxn := func() transactions.Transaction {
1551+
return transactions.Transaction{
1552+
Type: protocol.PaymentTx,
1553+
Header: transactions.Header{
1554+
Sender: sender,
1555+
Fee: basics.MicroAlgos{Raw: proto.MinTxnFee},
1556+
FirstValid: 1,
1557+
LastValid: 100,
1558+
GenesisHash: blockHeader.GenesisHash,
1559+
},
1560+
PaymentTxnFields: transactions.PaymentTxnFields{
1561+
Receiver: addrs[2],
1562+
Amount: basics.MicroAlgos{Raw: 1000},
1563+
},
1564+
}
1565+
}
1566+
1567+
// Test 1: AuthAddr == Sender
1568+
tx1 := makeTxn()
1569+
stxn := tx1.Sign(secrets[0])
1570+
stxn.AuthAddr = sender
1571+
groupCtx, err := PrepareGroupContext([]transactions.SignedTxn{stxn}, &blockHeader, nil, nil)
1572+
require.NoError(t, err)
1573+
err = verifyTxn(0, groupCtx)
1574+
if enforceEnabled {
1575+
require.ErrorContains(t, err, "AuthAddr must be different from Sender",
1576+
"AuthAddr == Sender should be rejected when enforcement is enabled")
1577+
} else {
1578+
require.NoError(t, err,
1579+
"AuthAddr == Sender should be allowed when enforcement is disabled")
1580+
}
1581+
1582+
// Test 2: Empty AuthAddr should always be allowed
1583+
tx2 := makeTxn()
1584+
stxn = tx2.Sign(secrets[0])
1585+
stxn.AuthAddr = basics.Address{}
1586+
groupCtx, err = PrepareGroupContext([]transactions.SignedTxn{stxn}, &blockHeader, nil, nil)
1587+
require.NoError(t, err)
1588+
err = verifyTxn(0, groupCtx)
1589+
require.NoError(t, err, "empty AuthAddr should always be allowed")
1590+
1591+
// Test 3: AuthAddr != Sender should pass the check (legitimate rekeying case)
1592+
// Sign with otherAddr's key to pass signature verification
1593+
tx3 := makeTxn()
1594+
stxn = tx3.Sign(secrets[1]) // Sign with secrets[1] which corresponds to otherAddr
1595+
stxn.AuthAddr = otherAddr
1596+
groupCtx, err = PrepareGroupContext([]transactions.SignedTxn{stxn}, &blockHeader, nil, nil)
1597+
require.NoError(t, err)
1598+
err = verifyTxn(0, groupCtx)
1599+
require.NoError(t, err, "AuthAddr != Sender should pass verification (legitimate rekeying)")
1600+
}

protocol/consensus.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ const DEPRECATEDConsensusV3 = ConsensusVersion("v3")
4747
// closes out an account.
4848
const DEPRECATEDConsensusV4 = ConsensusVersion("v4")
4949

50-
// DEPRECATEDConsensusV5 sets MinTxnFee to 1000 and fixes a blance lookback bug
50+
// DEPRECATEDConsensusV5 sets MinTxnFee to 1000 and fixes a balance lookback bug
5151
const DEPRECATEDConsensusV5 = ConsensusVersion("v5")
5252

5353
// DEPRECATEDConsensusV6 adds support for explicit ephemeral-key parameters

0 commit comments

Comments
 (0)