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

simulate: fix signers #5942

Merged
merged 30 commits into from
Jun 14, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
141ab94
Simulate auth addr trickery
jasonpaulos Jan 23, 2024
a3abdc9
initial fix auth addrs implementation
joe-p Feb 23, 2024
404372c
only fix auth addrs for txns that have no sig
joe-p Apr 26, 2024
e2df845
fix linting error
joe-p Apr 29, 2024
af154f0
FixSigners in ResultEvalOverrides
joe-p May 1, 2024
24e8150
test signing various transactions for fix signers
joe-p May 1, 2024
4179503
fix signPayAfterInnerRekey
joe-p May 1, 2024
6cbf130
fix txnHasNoSignature location
joe-p May 1, 2024
a923d71
rm some whitespace
joe-p May 1, 2024
52212d3
check signer in simulate response
joe-p May 1, 2024
9e2e377
more explicit error message checking
joe-p May 1, 2024
156c3e9
FixedSigner in TxnResult
joe-p May 1, 2024
b404899
don't set FixedSigner after failed txn
joe-p May 2, 2024
97970a5
stop static rekey correction after first appl
joe-p May 2, 2024
01ed949
rm TODO comment
joe-p May 2, 2024
16dcb0d
skip setting the FixedSigner when signer == sender
joe-p May 22, 2024
b3adf66
API implementation (not working)
joe-p May 30, 2024
7c494a8
Merge branch 'master' into pr/joe-p/5942
jasonpaulos May 31, 2024
4dc8e62
Merge pull request #1 from jasonpaulos/master-simulate-auth-addr
joe-p Jun 3, 2024
e8cec95
fix conversion of simulate request with FixSigners (resp WIP)
joe-p Jun 3, 2024
8bf0954
don't use fixed zero address for fixed signer
joe-p Jun 4, 2024
8fa89c6
test comments
joe-p Jun 4, 2024
89c20b5
only FixSigners in AfterProgram for outer app calls
joe-p Jun 4, 2024
7dc3716
more inner txn rekey nesting in test
joe-p Jun 4, 2024
1eaf837
Test changes and tweaks
jasonpaulos Jun 6, 2024
c2b9dfb
Merge pull request #3 from jasonpaulos/fix-signers-sub-pr
joe-p Jun 6, 2024
0c018a9
use txnHasNoSignature
joe-p Jun 11, 2024
65cbc51
refactor FixSigner logic in AfterProgram
joe-p Jun 11, 2024
d6e5884
Merge branch 'master' into simulate-auth-addr
joe-p Jun 13, 2024
cf0272f
only lookup account if txn has no sig
joe-p Jun 14, 2024
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
71 changes: 71 additions & 0 deletions ledger/simulation/simulation_eval_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8896,3 +8896,74 @@ func TestUnnamedResourcesCrossProductLimits(t *testing.T) {
})
}
}

func TestFixAuthAddr(t *testing.T) {
partitiontest.PartitionTest(t)
t.Parallel()

env := simulationtesting.PrepareSimulatorTest(t)
defer env.Close()

sender := env.Accounts[0]
other := env.Accounts[1]

appID := env.CreateApp(sender.Addr, simulationtesting.AppParams{
ApprovalProgram: `#pragma version 9
txn ApplicationID
bz end

itxn_begin
int pay
itxn_field TypeEnum
txn ApplicationArgs 0
itxn_field Sender
txn ApplicationArgs 0
sha512_256 // essentially a random account
itxn_field RekeyTo
itxn_submit

end:
int 1
`,
ClearStateProgram: "#pragma version 9\nint 1",
})
env.TransferAlgos(sender.Addr, appID.Address(), 1_000_000)

pay0 := env.TxnInfo.NewTxn(txntest.Txn{
Type: protocol.PaymentTx,
Sender: sender.Addr,
Receiver: sender.Addr,
RekeyTo: other.Addr,
})
pay1 := env.TxnInfo.NewTxn(txntest.Txn{
Type: protocol.PaymentTx,
Sender: sender.Addr,
Receiver: sender.Addr,
RekeyTo: appID.Address(),
})
appCall := env.TxnInfo.NewTxn(txntest.Txn{
Type: protocol.ApplicationCallTx,
Sender: other.Addr,
ApplicationID: appID,
ApplicationArgs: [][]byte{sender.Addr[:]},
})
pay2 := env.TxnInfo.NewTxn(txntest.Txn{
Type: protocol.PaymentTx,
Sender: sender.Addr,
Receiver: sender.Addr,
})

txgroup := txntest.Group(&pay0, &pay1, &appCall, &pay2)

request := simulation.Request{
TxnGroups: [][]transactions.SignedTxn{txgroup},
AllowEmptySignatures: true,
}

result, err := simulation.MakeSimulator(env.Ledger, false).Simulate(request)
require.NoError(t, err)

require.Empty(t, result.TxnGroups[0].FailureMessage)

// TODO: make sure output txn has the correct auth addr?
}
54 changes: 41 additions & 13 deletions ledger/simulation/simulator.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@
// check verifies that the transaction is well-formed and has valid or missing signatures.
// An invalid transaction group error is returned if the transaction is not well-formed or there are invalid signatures.
// To make things easier, we support submitting unsigned transactions and will respond whether signatures are missing.
func (s Simulator) check(hdr bookkeeping.BlockHeader, txgroup []transactions.SignedTxn, tracer logic.EvalTracer, overrides ResultEvalOverrides) error {
func (s Simulator) check(hdr bookkeeping.BlockHeader, txgroup []transactions.SignedTxnWithAD, tracer logic.EvalTracer, overrides ResultEvalOverrides) error {
proxySignerSecrets, err := crypto.SecretKeyToSignatureSecrets(proxySigner)
if err != nil {
return err
Expand All @@ -158,7 +158,8 @@
// denoting that a LogicSig's delegation signature is omitted, e.g. by setting all the bits of
// the signature.
txnsToVerify := make([]transactions.SignedTxn, len(txgroup))
for i, stxn := range txgroup {
for i, stxnad := range txgroup {
stxn := stxnad.SignedTxn
if stxn.Txn.Type == protocol.StateProofTx {
return errors.New("cannot simulate StateProof transactions")
}
Expand All @@ -181,16 +182,14 @@
return err
}

func (s Simulator) evaluate(hdr bookkeeping.BlockHeader, stxns []transactions.SignedTxn, tracer logic.EvalTracer) (*ledgercore.ValidatedBlock, error) {
func (s Simulator) evaluate(hdr bookkeeping.BlockHeader, group []transactions.SignedTxnWithAD, tracer logic.EvalTracer) (*ledgercore.ValidatedBlock, error) {
// s.ledger has 'StartEvaluator' because *data.Ledger is embedded in the simulatorLedger
// and data.Ledger embeds *ledger.Ledger
eval, err := s.ledger.StartEvaluator(hdr, len(stxns), 0, tracer)
eval, err := s.ledger.StartEvaluator(hdr, len(group), 0, tracer)
if err != nil {
return nil, err
}

group := transactions.WrapSignedTxnsWithAD(stxns)

err = eval.TransactionGroup(group)
if err != nil {
return nil, EvalFailureError{SimulatorError{err}}
Expand All @@ -205,14 +204,41 @@
return vb, nil
}

func (s Simulator) simulateWithTracer(txgroup []transactions.SignedTxn, tracer logic.EvalTracer, overrides ResultEvalOverrides) (*ledgercore.ValidatedBlock, error) {
func (s Simulator) simulateWithTracer(txgroup []transactions.SignedTxnWithAD, tracer logic.EvalTracer, overrides ResultEvalOverrides) (*ledgercore.ValidatedBlock, error) {
prevBlockHdr, err := s.ledger.BlockHdr(s.ledger.start)
if err != nil {
return nil, err
}
nextBlock := bookkeeping.MakeBlock(prevBlockHdr)
hdr := nextBlock.BlockHeader

fixAuthAddr := true
if fixAuthAddr {
// Map of rekeys for senders in the group
staticRekeys := make(map[basics.Address]basics.Address)

for i := range txgroup {
sender := txgroup[i].SignedTxn.Txn.Sender

if authAddr, ok := staticRekeys[sender]; ok {
// If there is a static rekey for the sender set the auth addr to that address
txgroup[i].SignedTxn.AuthAddr = authAddr
joe-p marked this conversation as resolved.
Show resolved Hide resolved
} else {
// Otherwise lookup the sender's account and set the txn auth addr to the account's auth addr
data, _, _, err := s.ledger.LookupAccount(s.ledger.start, sender)

Check failure on line 228 in ledger/simulation/simulator.go

View workflow job for this annotation

GitHub Actions / reviewdog-errors

[Lint Errors] reported by reviewdog 🐶 shadow: declaration of "err" shadows declaration at line 208 (govet) Raw Output: ledger/simulation/simulator.go:228:17: shadow: declaration of "err" shadows declaration at line 208 (govet) data, _, _, err := s.ledger.LookupAccount(s.ledger.start, sender) ^
if err != nil {
return nil, err
}
txgroup[i].SignedTxn.AuthAddr = data.AuthAddr
}

if txgroup[i].SignedTxn.Txn.RekeyTo != (basics.Address{}) {
staticRekeys[sender] = txgroup[i].SignedTxn.Txn.RekeyTo
}
}

}

// check that the transaction is well-formed and mark whether signatures are missing
err = s.check(hdr, txgroup, tracer, overrides)
if err != nil {
Expand Down Expand Up @@ -243,11 +269,6 @@
s.ledger.start = s.ledger.Ledger.Latest()
}

simulatorTracer, err := makeEvalTracer(s.ledger.start, simulateRequest, s.developerAPI)
if err != nil {
return Result{}, err
}

if len(simulateRequest.TxnGroups) != 1 {
return Result{}, InvalidRequestError{
SimulatorError{
Expand All @@ -256,7 +277,14 @@
}
}

block, err := s.simulateWithTracer(simulateRequest.TxnGroups[0], simulatorTracer, simulatorTracer.result.EvalOverrides)
group := transactions.WrapSignedTxnsWithAD(simulateRequest.TxnGroups[0])

simulatorTracer, err := makeEvalTracer(s.ledger.start, group, simulateRequest, s.developerAPI)
if err != nil {
return Result{}, err
}

block, err := s.simulateWithTracer(group, simulatorTracer, simulatorTracer.result.EvalOverrides)
if err != nil {
var verifyError *verify.TxGroupError
switch {
Expand Down
2 changes: 1 addition & 1 deletion ledger/simulation/simulator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ int 1`,

mockTracer := &mocktracer.Tracer{}
s.ledger.start = s.ledger.Ledger.Latest() // Set starting round for simulation
block, err := s.simulateWithTracer(txgroup, mockTracer, ResultEvalOverrides{})
block, err := s.simulateWithTracer(transactions.WrapSignedTxnsWithAD(txgroup), mockTracer, ResultEvalOverrides{})
require.NoError(t, err)

evalBlock := block.Block()
Expand Down
44 changes: 42 additions & 2 deletions ledger/simulation/tracer.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,14 +98,16 @@ type evalTracer struct {
// scratchSlots are the scratch slots changed on current opcode (currently either `store` or `stores`).
// NOTE: this field scratchSlots is used only for scratch change exposure.
scratchSlots []uint64

groups [][]transactions.SignedTxnWithAD
}

func makeEvalTracer(lastRound basics.Round, request Request, developerAPI bool) (*evalTracer, error) {
func makeEvalTracer(lastRound basics.Round, group []transactions.SignedTxnWithAD, request Request, developerAPI bool) (*evalTracer, error) {
result, err := makeSimulationResult(lastRound, request, developerAPI)
if err != nil {
return nil, err
}
return &evalTracer{result: &result}, nil
return &evalTracer{result: &result, groups: [][]transactions.SignedTxnWithAD{group}}, nil
}

// handleError is responsible for setting the failedAt field properly.
Expand Down Expand Up @@ -512,4 +514,42 @@ func (tracer *evalTracer) AfterProgram(cx *logic.EvalContext, pass bool, evalErr
} else {
tracer.handleError(evalError)
}

fixAuthAddrs := true

// Since an app could rekey multiple accounts, we need to go over the
// rest of the txngroup and make sure all the auth addrs are correct
if fixAuthAddrs {
knownAuthAddrs := make(map[basics.Address]basics.Address)
// iterate over all txns in the group after this one
for i := groupIndex + 1; i < len(cx.TxnGroup); i++ {
stxn := &tracer.groups[0][i]
sender := stxn.Txn.Sender

// Check if we already know the auth addr
if authAddr, ok := knownAuthAddrs[sender]; ok {
stxn.AuthAddr = authAddr
} else {
// Get the auth addr from the ledger
data, err := cx.Ledger.AccountData(sender)
if err != nil {
panic(err)
}

// set the txn auth addr
stxn.AuthAddr = data.AuthAddr
knownAuthAddrs[sender] = data.AuthAddr
}

// If this is an appl, we can break since we know AfterProgram will be called afterwards
if stxn.Txn.Type == protocol.ApplicationCallTx {
break
}

// If this is a rekey, save the auth addr for the sender
if stxn.Txn.RekeyTo != (basics.Address{}) {
knownAuthAddrs[sender] = stxn.Txn.RekeyTo
}
}
}
}
Loading