github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/teams/audit_test.go (about)

     1  package teams
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"sync"
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/keybase/client/go/teams/hidden"
    11  
    12  	"github.com/keybase/client/go/libkb"
    13  	"github.com/keybase/client/go/protocol/keybase1"
    14  	"github.com/stretchr/testify/require"
    15  )
    16  
    17  // See CORE-8860. We should be able to audit a stale team. That is, ever the merkle tree
    18  // is advertising a tail at 5, and we're only loaded through 3 (due to an unbusted cache),
    19  // the audit should still succeed.
    20  func TestAuditStaleTeam(t *testing.T) {
    21  
    22  	fus, tcs, cleanup := setupNTests(t, 5)
    23  	defer cleanup()
    24  
    25  	t.Logf("create team")
    26  	teamName, _ := createTeam2(*tcs[0])
    27  	m := make([]libkb.MetaContext, 5)
    28  	for i, tc := range tcs {
    29  		m[i] = libkb.NewMetaContextForTest(*tc)
    30  	}
    31  
    32  	// We set up codenames for 3 users, A, B, C, D and E
    33  	const (
    34  		A = 0
    35  		B = 1
    36  		C = 2
    37  		D = 3
    38  		E = 4
    39  	)
    40  
    41  	t.Logf("A adds B to the team as an admin")
    42  	_, err := AddMember(m[0].Ctx(), tcs[0].G, teamName.String(), fus[1].Username, keybase1.TeamRole_ADMIN, nil)
    43  	require.NoError(t, err)
    44  
    45  	load := func(asUser int) {
    46  		_, err = Load(m[asUser].Ctx(), tcs[asUser].G, keybase1.LoadTeamArg{
    47  			Name:    teamName.String(),
    48  			Public:  false,
    49  			StaleOK: true,
    50  		})
    51  		require.NoError(t, err)
    52  	}
    53  
    54  	addC := func(asUser int) {
    55  		_, err = AddMember(m[asUser].Ctx(), tcs[asUser].G, teamName.String(), fus[C].Username, keybase1.TeamRole_READER, nil)
    56  		require.NoError(t, err)
    57  	}
    58  
    59  	addD := func(asUser int) {
    60  		_, err = AddMember(m[asUser].Ctx(), tcs[asUser].G, teamName.String(), fus[D].Username, keybase1.TeamRole_BOT, nil)
    61  		require.NoError(t, err)
    62  	}
    63  
    64  	addE := func(asUser int) {
    65  		_, err = AddMember(m[asUser].Ctx(), tcs[asUser].G, teamName.String(), fus[E].Username, keybase1.TeamRole_RESTRICTEDBOT, &keybase1.TeamBotSettings{})
    66  		require.NoError(t, err)
    67  	}
    68  
    69  	rmC := func(asUser int) {
    70  		err = RemoveMember(m[asUser].Ctx(), tcs[asUser].G, teamName.String(), fus[C].Username)
    71  		require.NoError(t, err)
    72  	}
    73  
    74  	rmD := func(asUser int) {
    75  		err = RemoveMember(m[asUser].Ctx(), tcs[asUser].G, teamName.String(), fus[D].Username)
    76  		require.NoError(t, err)
    77  	}
    78  
    79  	rmE := func(asUser int) {
    80  		err = RemoveMember(m[asUser].Ctx(), tcs[asUser].G, teamName.String(), fus[E].Username)
    81  		require.NoError(t, err)
    82  	}
    83  
    84  	setFastAudits := func(m libkb.MetaContext) {
    85  		// do a lot of probes so we're likely to find issues
    86  		m.G().Env.Test.TeamAuditParams = &libkb.TeamAuditParams{
    87  			NumPostProbes:         10,
    88  			MerkleMovementTrigger: keybase1.Seqno(1),
    89  			RootFreshness:         time.Duration(1),
    90  			LRUSize:               500,
    91  			NumPreProbes:          3,
    92  			Parallelism:           3,
    93  		}
    94  	}
    95  
    96  	setSlowAudits := func(m libkb.MetaContext) {
    97  		m.G().Env.Test.TeamAuditParams = &libkb.TeamAuditParams{
    98  			NumPostProbes:         1,
    99  			MerkleMovementTrigger: keybase1.Seqno(1000000),
   100  			RootFreshness:         time.Hour,
   101  			LRUSize:               500,
   102  			NumPreProbes:          3,
   103  			Parallelism:           3,
   104  		}
   105  	}
   106  
   107  	// A adds C, D and E to the team and triggers an Audit
   108  	setFastAudits(m[A])
   109  	addC(A)
   110  	addD(A)
   111  	addE(A)
   112  
   113  	// A removes C, D and E from the team, and loads the team, but does *not* trigger an audit
   114  	setSlowAudits(m[A])
   115  	rmC(A)
   116  	rmD(A)
   117  	rmE(A)
   118  	load(A)
   119  
   120  	t.Logf("User B rotates the key a bunch of times")
   121  
   122  	// B rotates the key by adding and remove C a bunch of times.
   123  	for i := 0; i < 3; i++ {
   124  		addC(B)
   125  		rmC(B)
   126  	}
   127  
   128  	// A updates to the latest merkle root.
   129  	_, err = tcs[A].G.MerkleClient.FetchRootFromServer(m[A], 0)
   130  	require.NoError(t, err)
   131  
   132  	// A forces an audit on a stale team.
   133  	setFastAudits(m[A])
   134  	t.Logf("User A loading the team, and auditing on an primed cached")
   135  	load(A)
   136  }
   137  
   138  func TestAuditRotateAudit(t *testing.T) {
   139  	fus, tcs, cleanup := setupNTests(t, 4)
   140  	defer cleanup()
   141  
   142  	t.Logf("create team")
   143  	teamName, teamID := createTeam2(*tcs[0])
   144  	m := make([]libkb.MetaContext, 4)
   145  	for i, tc := range tcs {
   146  		m[i] = libkb.NewMetaContextForTest(*tc)
   147  	}
   148  
   149  	// We set up codenames for 3 users, A, B, C, and D
   150  	const (
   151  		A = 0
   152  		B = 1
   153  		C = 2
   154  		D = 3
   155  	)
   156  
   157  	load := func() {
   158  		_, err := Load(m[A].Ctx(), tcs[A].G, keybase1.LoadTeamArg{
   159  			Name:        teamName.String(),
   160  			Public:      false,
   161  			ForceRepoll: true,
   162  		})
   163  		require.NoError(t, err)
   164  	}
   165  
   166  	addB := func() keybase1.Seqno {
   167  		_, err := AddMember(m[A].Ctx(), tcs[A].G, teamName.String(), fus[B].Username, keybase1.TeamRole_READER, nil)
   168  		require.NoError(t, err)
   169  		return 1
   170  	}
   171  
   172  	addC := func() keybase1.Seqno {
   173  		_, err := AddMember(m[A].Ctx(), tcs[A].G, teamName.String(), fus[C].Username, keybase1.TeamRole_BOT, nil)
   174  		require.NoError(t, err)
   175  		return 1
   176  	}
   177  
   178  	addD := func() keybase1.Seqno {
   179  		_, err := AddMember(m[A].Ctx(), tcs[A].G, teamName.String(), fus[D].Username, keybase1.TeamRole_RESTRICTEDBOT, &keybase1.TeamBotSettings{})
   180  		require.NoError(t, err)
   181  		// adding a RESTRICTEDBOT adds an additional bot_settings link
   182  		return 2
   183  	}
   184  
   185  	rmB := func() keybase1.Seqno {
   186  		err := RemoveMember(m[A].Ctx(), tcs[A].G, teamName.String(), fus[B].Username)
   187  		require.NoError(t, err)
   188  		return 1
   189  	}
   190  
   191  	rmC := func() keybase1.Seqno {
   192  		err := RemoveMember(m[A].Ctx(), tcs[A].G, teamName.String(), fus[C].Username)
   193  		require.NoError(t, err)
   194  		return 1
   195  	}
   196  
   197  	rmD := func() keybase1.Seqno {
   198  		err := RemoveMember(m[A].Ctx(), tcs[A].G, teamName.String(), fus[D].Username)
   199  		require.NoError(t, err)
   200  		return 1
   201  	}
   202  
   203  	setFastAudits := func() {
   204  		// do a lot of probes so we're likely to find issues
   205  		m[A].G().Env.Test.TeamAuditParams = &libkb.TeamAuditParams{
   206  			NumPostProbes:         10,
   207  			MerkleMovementTrigger: keybase1.Seqno(1),
   208  			RootFreshness:         time.Duration(1),
   209  			LRUSize:               500,
   210  			NumPreProbes:          3,
   211  			Parallelism:           3,
   212  		}
   213  	}
   214  
   215  	assertAuditTo := func(n keybase1.Seqno) {
   216  		auditor := m[A].G().GetTeamAuditor().(*Auditor)
   217  		history, err := auditor.getFromCache(m[A], teamID, auditor.getLRU())
   218  		require.NoError(t, err)
   219  		require.Equal(t, n, lastAudit(history).MaxChainSeqno)
   220  	}
   221  
   222  	setFastAudits()
   223  	actions := []func() keybase1.Seqno{addB, rmB, addC, rmC, addD, rmD}
   224  	expectedSeqno := keybase1.Seqno(1)
   225  	for _, action := range actions {
   226  		expectedSeqno += action()
   227  		load()
   228  		assertAuditTo(expectedSeqno)
   229  	}
   230  }
   231  
   232  type CorruptingMerkleClient struct {
   233  	libkb.MerkleClientInterface
   234  
   235  	corruptor func(leaf *libkb.MerkleGenericLeaf, root *libkb.MerkleRoot, hiddenResp *libkb.MerkleHiddenResponse, err error) (*libkb.MerkleGenericLeaf, *libkb.MerkleRoot, *libkb.MerkleHiddenResponse, error)
   236  }
   237  
   238  func (c CorruptingMerkleClient) LookupLeafAtSeqnoForAudit(m libkb.MetaContext, leafID keybase1.UserOrTeamID, s keybase1.Seqno, processHiddenResponseFunc libkb.ProcessHiddenRespFunc) (leaf *libkb.MerkleGenericLeaf, root *libkb.MerkleRoot, hiddenResp *libkb.MerkleHiddenResponse, err error) {
   239  	return c.corruptor(c.MerkleClientInterface.LookupLeafAtSeqnoForAudit(m, leafID, s, processHiddenResponseFunc))
   240  }
   241  
   242  var _ libkb.MerkleClientInterface = CorruptingMerkleClient{}
   243  
   244  type MockRandom struct {
   245  	libkb.SecureRandom
   246  	nextOutputs []int64
   247  	t           *testing.T
   248  }
   249  
   250  func (m *MockRandom) RndRange(lo, hi int64) (int64, error) {
   251  	// pop and return the first output which is appropriate
   252  	for i, n := range m.nextOutputs {
   253  		if lo <= n && n <= hi {
   254  			m.nextOutputs = append(m.nextOutputs[0:i], m.nextOutputs[i+1:]...)
   255  			m.t.Logf("MockRandom: Output %v in range %v,%v (have %v left)", n, lo, hi, m.nextOutputs)
   256  			return n, nil
   257  		}
   258  	}
   259  	return 0, fmt.Errorf("MockRandom: output not found in range %v,%v (have %v left)", lo, hi, m.nextOutputs)
   260  }
   261  
   262  var _ libkb.Random = (*MockRandom)(nil)
   263  
   264  func TestAuditFailsIfDataIsInconsistent(t *testing.T) {
   265  	fus, tcs, cleanup := setupNTests(t, 3)
   266  	defer cleanup()
   267  
   268  	t.Logf("create team")
   269  	teamName, teamID := createTeam2(*tcs[0])
   270  	m := make([]libkb.MetaContext, 3)
   271  	for i, tc := range tcs {
   272  		m[i] = libkb.NewMetaContextForTest(*tc)
   273  	}
   274  
   275  	// We set up codenames for 3 users, A, B, C
   276  	const (
   277  		A = 0
   278  		B = 1
   279  		C = 2
   280  	)
   281  
   282  	add := func(adder, addee int) keybase1.Seqno {
   283  		_, err := AddMember(m[adder].Ctx(), tcs[adder].G, teamName.String(), fus[addee].Username, keybase1.TeamRole_READER, nil)
   284  		require.NoError(t, err)
   285  		return 1
   286  	}
   287  
   288  	setAudits := func(user int) {
   289  		m[user].G().Env.Test.TeamAuditParams = &libkb.TeamAuditParams{
   290  			NumPostProbes:         3,
   291  			MerkleMovementTrigger: keybase1.Seqno(1),
   292  			RootFreshness:         time.Duration(1),
   293  			LRUSize:               500,
   294  			NumPreProbes:          3,
   295  			Parallelism:           3,
   296  		}
   297  	}
   298  
   299  	assertAuditTo := func(user int, mainSeqno, hiddenSeqno keybase1.Seqno) {
   300  		auditor := m[user].G().GetTeamAuditor().(*Auditor)
   301  		history, err := auditor.getFromCache(m[user], teamID, auditor.getLRU())
   302  		require.NoError(t, err)
   303  		require.Equal(t, mainSeqno, lastAudit(history).MaxChainSeqno)
   304  		require.Equal(t, hiddenSeqno, lastAudit(history).MaxHiddenSeqno)
   305  	}
   306  
   307  	setAudits(B)
   308  
   309  	// A adds B to the team
   310  	add(A, B)
   311  
   312  	makeHiddenRotation(t, m[A].G(), teamName)
   313  	requestNewBlindTreeFromArchitectAndWaitUntilDone(t, tcs[A])
   314  	makeHiddenRotation(t, m[A].G(), teamName)
   315  	requestNewBlindTreeFromArchitectAndWaitUntilDone(t, tcs[A])
   316  	add(A, C)
   317  
   318  	team, err := GetForTestByStringName(context.TODO(), m[A].G(), teamName.String())
   319  	require.NoError(t, err)
   320  
   321  	headMerkleSeqno := int64(team.MainChain().Chain.HeadMerkle.Seqno)
   322  	t.Logf("headMerkleSeqno: %v", headMerkleSeqno)
   323  
   324  	firstWithHiddenS, err := m[A].G().GetMerkleClient().FirstMainRootWithHiddenRootHash(m[A])
   325  	require.NoError(t, err)
   326  	firstWithHidden := int64(firstWithHiddenS)
   327  	t.Logf("firstWithHidden: %v", firstWithHidden)
   328  	root := m[A].G().GetMerkleClient().LastRoot(m[A])
   329  	require.NotNil(t, root)
   330  	high := int64(*root.Seqno())
   331  	t.Logf("latest root: %v %X", root.Seqno(), root.HashMeta())
   332  
   333  	for i := headMerkleSeqno; i <= high; i++ {
   334  		leaf, _, hiddenResp, err := m[B].G().GetMerkleClient().LookupLeafAtSeqnoForAudit(m[B], teamID.AsUserOrTeam(), keybase1.Seqno(i), hidden.ProcessHiddenResponseFunc)
   335  		require.NoError(t, err)
   336  		if leaf != nil && leaf.Private != nil && len(leaf.Private.LinkID) > 0 {
   337  			t.Logf("Seqno %v Leaf %v Hidden %v", i, leaf.Private.Seqno, hiddenResp)
   338  		} else {
   339  			t.Logf("Seqno %v Leaf EMPTY Hidden %v", i, hiddenResp)
   340  		}
   341  
   342  	}
   343  
   344  	merkle := m[B].G().GetMerkleClient()
   345  	rand := m[B].G().GetRandom()
   346  
   347  	corruptMerkle := CorruptingMerkleClient{
   348  		MerkleClientInterface: merkle,
   349  		corruptor: func(leaf *libkb.MerkleGenericLeaf, root *libkb.MerkleRoot, hiddenResp *libkb.MerkleHiddenResponse, err error) (*libkb.MerkleGenericLeaf, *libkb.MerkleRoot, *libkb.MerkleHiddenResponse, error) {
   350  			t.Logf("Corruptor: received %v,%v,%v,%v", leaf, root, hiddenResp, err)
   351  			if leaf != nil && leaf.Private != nil && len(leaf.Private.LinkID) > 0 {
   352  				leaf.Private.LinkID[0] ^= 0xff
   353  				t.Logf("Corruptor: altering LINKID for %v", leaf.Private.Seqno)
   354  			}
   355  			return leaf, root, hiddenResp, err
   356  		},
   357  	}
   358  	m[B].G().SetMerkleClient(corruptMerkle)
   359  	m[B].G().SetRandom(&MockRandom{t: t, nextOutputs: []int64{firstWithHidden - 1, firstWithHidden, firstWithHidden + 1, headMerkleSeqno, headMerkleSeqno + 1, high - 1}})
   360  
   361  	auditor := m[B].G().GetTeamAuditor().(*Auditor)
   362  	err = auditor.AuditTeam(m[B], teamID, false, team.MainChain().Chain.HeadMerkle.Seqno, team.MainChain().Chain.LinkIDs, team.HiddenChain().GetOuter(), team.MainChain().Chain.LastSeqno, team.HiddenChain().GetLastCommittedSeqno(), root, keybase1.AuditMode_STANDARD)
   363  	require.Error(t, err)
   364  	require.IsType(t, AuditError{}, err)
   365  	require.Contains(t, err.Error(), "team chain linkID mismatch")
   366  
   367  	// repeat a second time to ensure that a failed audit is not cached (and thus skipped the second time)
   368  	m[B].G().SetRandom(&MockRandom{t: t, nextOutputs: []int64{firstWithHidden - 1, firstWithHidden, firstWithHidden + 1, headMerkleSeqno, headMerkleSeqno + 1, high - 1}})
   369  	err = auditor.AuditTeam(m[B], teamID, false, team.MainChain().Chain.HeadMerkle.Seqno, team.MainChain().Chain.LinkIDs, team.HiddenChain().GetOuter(), team.MainChain().Chain.LastSeqno, team.HiddenChain().GetLastCommittedSeqno(), root, keybase1.AuditMode_STANDARD)
   370  	require.Error(t, err)
   371  	require.IsType(t, AuditError{}, err)
   372  	require.Contains(t, err.Error(), "team chain linkID mismatch")
   373  
   374  	corruptMerkle = CorruptingMerkleClient{
   375  		MerkleClientInterface: merkle,
   376  		corruptor: func(leaf *libkb.MerkleGenericLeaf, root *libkb.MerkleRoot, hiddenResp *libkb.MerkleHiddenResponse, err error) (*libkb.MerkleGenericLeaf, *libkb.MerkleRoot, *libkb.MerkleHiddenResponse, error) {
   377  			t.Logf("Corruptor: received %v,%v,%v,%v", leaf, root, hiddenResp, err)
   378  			if leaf != nil && leaf.Private != nil && len(leaf.Private.LinkID) > 0 {
   379  				leaf.Private.Seqno += 5
   380  				t.Logf("Corruptor: altering Seqno, leaf = %+v", leaf)
   381  			}
   382  			return leaf, root, hiddenResp, err
   383  		},
   384  	}
   385  	m[B].G().SetMerkleClient(corruptMerkle)
   386  	m[B].G().SetRandom(&MockRandom{t: t, nextOutputs: []int64{firstWithHidden - 1, firstWithHidden, firstWithHidden + 1, headMerkleSeqno, headMerkleSeqno + 1, high - 1}})
   387  
   388  	err = auditor.AuditTeam(m[B], teamID, false, team.MainChain().Chain.HeadMerkle.Seqno, team.MainChain().Chain.LinkIDs, team.HiddenChain().GetOuter(), team.MainChain().Chain.LastSeqno, team.HiddenChain().GetLastCommittedSeqno(), root, keybase1.AuditMode_STANDARD)
   389  	require.Error(t, err)
   390  	require.IsType(t, AuditError{}, err)
   391  	require.Contains(t, err.Error(), "team chain rollback")
   392  
   393  	// now, let's try to mess with the preProbes, by making it appear as if the team existed before it was actually created.
   394  	corruptMerkle = CorruptingMerkleClient{
   395  		MerkleClientInterface: merkle,
   396  		corruptor: func(leaf *libkb.MerkleGenericLeaf, root *libkb.MerkleRoot, hiddenResp *libkb.MerkleHiddenResponse, err error) (*libkb.MerkleGenericLeaf, *libkb.MerkleRoot, *libkb.MerkleHiddenResponse, error) {
   397  			t.Logf("Corruptor: received %v,%v,%v,%v", leaf, root, hiddenResp, err)
   398  			if leaf == nil {
   399  				leaf = &libkb.MerkleGenericLeaf{
   400  					LeafID: teamID.AsUserOrTeam(),
   401  				}
   402  			}
   403  			if leaf.Private == nil {
   404  				t.Logf("Corruptor: creating a fake leaf when there should have been none")
   405  				leaf.Private = &libkb.MerkleTriple{
   406  					Seqno:  4,
   407  					LinkID: []byte{0x00, 0x01, 0x02},
   408  				}
   409  			}
   410  			return leaf, root, hiddenResp, err
   411  		},
   412  	}
   413  	m[B].G().SetMerkleClient(corruptMerkle)
   414  	m[B].G().SetRandom(&MockRandom{t: t, nextOutputs: []int64{firstWithHidden - 1, firstWithHidden, firstWithHidden + 1, headMerkleSeqno, headMerkleSeqno + 1, high - 1}})
   415  
   416  	err = auditor.AuditTeam(m[B], teamID, false, team.MainChain().Chain.HeadMerkle.Seqno, team.MainChain().Chain.LinkIDs, team.HiddenChain().GetOuter(), team.MainChain().Chain.LastSeqno, team.HiddenChain().GetLastCommittedSeqno(), root, keybase1.AuditMode_STANDARD)
   417  	require.Error(t, err)
   418  	require.IsType(t, AuditError{}, err)
   419  	require.Contains(t, err.Error(), "merkle root should not have had a leaf for team")
   420  
   421  	// with the original merkle client (i.e. when the server response is not altered), the audit should succeed
   422  	m[B].G().SetMerkleClient(merkle)
   423  	m[B].G().SetRandom(rand)
   424  	err = auditor.AuditTeam(m[B], teamID, false, team.MainChain().Chain.HeadMerkle.Seqno, team.MainChain().Chain.LinkIDs, team.HiddenChain().GetOuter(), team.MainChain().Chain.LastSeqno, team.HiddenChain().GetLastCommittedSeqno(), root, keybase1.AuditMode_STANDARD)
   425  	require.NoError(t, err)
   426  	assertAuditTo(B, 3, 0)
   427  }
   428  
   429  func TestFailedProbesAreRetried(t *testing.T) {
   430  	fus, tcs, cleanup := setupNTests(t, 2)
   431  	defer cleanup()
   432  
   433  	// We set up codenames for 2 users, A, B
   434  	const (
   435  		A = 0
   436  		B = 1
   437  	)
   438  
   439  	makePaperKey(t, tcs[A])
   440  	makePaperKey(t, tcs[B])
   441  
   442  	t.Logf("create team")
   443  	teamName, teamID := createTeam2(*tcs[A])
   444  	m := make([]libkb.MetaContext, 3)
   445  	for i, tc := range tcs {
   446  		m[i] = libkb.NewMetaContextForTest(*tc)
   447  	}
   448  
   449  	add := func(adder, addee int) keybase1.Seqno {
   450  		_, err := AddMember(m[adder].Ctx(), tcs[adder].G, teamName.String(), fus[addee].Username, keybase1.TeamRole_READER, nil)
   451  		require.NoError(t, err)
   452  		return 1
   453  	}
   454  
   455  	setFastAudits := func(user int) {
   456  		m[user].G().Env.Test.TeamAuditParams = &libkb.TeamAuditParams{
   457  			NumPostProbes:         2,
   458  			MerkleMovementTrigger: keybase1.Seqno(1),
   459  			RootFreshness:         time.Duration(1),
   460  			LRUSize:               500,
   461  			NumPreProbes:          2,
   462  			Parallelism:           3,
   463  		}
   464  	}
   465  
   466  	setFastAudits(B)
   467  
   468  	// A adds B to the team
   469  	add(A, B)
   470  
   471  	// make some extra merkle tree versions
   472  	makePaperKey(t, tcs[A])
   473  	makePaperKey(t, tcs[B])
   474  	makePaperKey(t, tcs[A])
   475  	makePaperKey(t, tcs[B])
   476  	makePaperKey(t, tcs[A])
   477  	makePaperKey(t, tcs[B])
   478  
   479  	team, err := GetForTestByStringName(context.TODO(), m[A].G(), teamName.String())
   480  	require.NoError(t, err)
   481  	root := m[A].G().GetMerkleClient().LastRoot(m[A])
   482  	require.NotNil(t, root)
   483  	latestRootSeqno := *root.Seqno()
   484  	t.Logf("latest root: %v %X", root.Seqno(), root.HashMeta())
   485  	headMerkleSeqno := team.MainChain().Chain.HeadMerkle.Seqno
   486  	t.Logf("headMerkleSeqno: %v", headMerkleSeqno)
   487  	firstWithHiddenS, err := m[A].G().GetMerkleClient().FirstMainRootWithHiddenRootHash(m[A])
   488  	require.NoError(t, err)
   489  	firstWithHidden := firstWithHiddenS
   490  	t.Logf("firstWithHidden: %v", firstWithHidden)
   491  
   492  	for i := headMerkleSeqno; i <= latestRootSeqno; i++ {
   493  		leaf, _, hiddenResp, err := m[B].G().GetMerkleClient().LookupLeafAtSeqnoForAudit(m[B], teamID.AsUserOrTeam(), i, hidden.ProcessHiddenResponseFunc)
   494  		require.NoError(t, err)
   495  		if leaf != nil && leaf.Private != nil && len(leaf.Private.LinkID) > 0 {
   496  			t.Logf("Seqno %v Leaf %v Hidden %v", i, leaf.Private.Seqno, hiddenResp)
   497  		} else {
   498  			t.Logf("Seqno %v Leaf EMPTY Hidden %v", i, hiddenResp)
   499  		}
   500  	}
   501  
   502  	auditor := m[B].G().GetTeamAuditor().(*Auditor)
   503  	lru := auditor.getLRU()
   504  
   505  	// no audits yet, history is nil.
   506  	history, err := auditor.getFromCache(m[B], teamID, lru)
   507  	require.NoError(t, err)
   508  	require.Nil(t, history)
   509  
   510  	merkle := m[B].G().GetMerkleClient()
   511  	rand := m[B].G().GetRandom()
   512  
   513  	// first we corrupt postProbes and test that those are retried
   514  	corruptMerkle := CorruptingMerkleClient{
   515  		MerkleClientInterface: merkle,
   516  		corruptor: func(leaf *libkb.MerkleGenericLeaf, root *libkb.MerkleRoot, hiddenResp *libkb.MerkleHiddenResponse, err error) (*libkb.MerkleGenericLeaf, *libkb.MerkleRoot, *libkb.MerkleHiddenResponse, error) {
   517  			t.Logf("Corruptor: received %v,%v,%v,%v", leaf, root, hiddenResp, err)
   518  			if *root.Seqno() > headMerkleSeqno {
   519  				if leaf != nil && leaf.Private != nil && len(leaf.Private.LinkID) > 0 {
   520  					leaf.Private.LinkID[0] ^= 0xff
   521  					t.Logf("Corruptor: altering LINKID for %v", leaf.Private.Seqno)
   522  				} else {
   523  					t.Logf("Corruptor: introducing leaf at roor %v", *root.Seqno())
   524  					leaf = &libkb.MerkleGenericLeaf{
   525  						LeafID:  teamID.AsUserOrTeam(),
   526  						Private: &libkb.MerkleTriple{Seqno: keybase1.Seqno(100)},
   527  					}
   528  				}
   529  			}
   530  			return leaf, root, hiddenResp, err
   531  		},
   532  	}
   533  	m[B].G().SetMerkleClient(corruptMerkle)
   534  	// the first two are for preprobes, the last for post probes
   535  	m[B].G().SetRandom(&MockRandom{t: t, nextOutputs: []int64{int64(firstWithHidden) - 1, int64(firstWithHidden + 1), int64(headMerkleSeqno) + 1, int64(headMerkleSeqno) + 2}})
   536  
   537  	err = auditor.AuditTeam(m[B], teamID, false, team.MainChain().Chain.HeadMerkle.Seqno, team.MainChain().Chain.LinkIDs, team.HiddenChain().GetOuter(), team.MainChain().Chain.LastSeqno, team.HiddenChain().GetLastCommittedSeqno(), root, keybase1.AuditMode_STANDARD)
   538  	require.Error(t, err)
   539  	require.IsType(t, AuditError{}, err)
   540  
   541  	history, err = auditor.getFromCache(m[B], teamID, lru)
   542  	require.NoError(t, err)
   543  	require.Len(t, history.PreProbesToRetry, 0)
   544  	require.Len(t, history.PostProbesToRetry, 2)
   545  	require.Contains(t, history.PostProbesToRetry, headMerkleSeqno+1)
   546  	require.Contains(t, history.PostProbesToRetry, headMerkleSeqno+2)
   547  
   548  	var probesToTestLock sync.Mutex
   549  	probesToTest := make(map[keybase1.Seqno]bool)
   550  	probesToTestLock.Lock()
   551  	probesToTest[headMerkleSeqno+1] = true
   552  	probesToTest[headMerkleSeqno+2] = true
   553  	numProbes := 2
   554  	probesToTestLock.Unlock()
   555  
   556  	corruptMerkle = CorruptingMerkleClient{
   557  		MerkleClientInterface: merkle,
   558  		corruptor: func(leaf *libkb.MerkleGenericLeaf, root *libkb.MerkleRoot, hiddenResp *libkb.MerkleHiddenResponse, err error) (*libkb.MerkleGenericLeaf, *libkb.MerkleRoot, *libkb.MerkleHiddenResponse, error) {
   559  			t.Logf("Corruptor: received %v,%v,%v,%v", leaf, root, hiddenResp, err)
   560  			probeSeqno := *root.Seqno()
   561  			if probeSeqno > headMerkleSeqno {
   562  				if probesToTest[probeSeqno] {
   563  					t.Logf("PostProbes: Seqno %v was retried", probeSeqno)
   564  					probesToTestLock.Lock()
   565  					probesToTest[probeSeqno] = false
   566  					numProbes--
   567  					probesToTestLock.Unlock()
   568  				}
   569  				if leaf != nil && leaf.Private != nil && len(leaf.Private.LinkID) > 0 {
   570  					leaf.Private.LinkID[0] ^= 0xff
   571  					t.Logf("Corruptor: altering LINKID for %v", leaf.Private.Seqno)
   572  				} else {
   573  					t.Logf("Corruptor: introducing leaf at roor %v", *root.Seqno())
   574  					leaf = &libkb.MerkleGenericLeaf{
   575  						LeafID:  teamID.AsUserOrTeam(),
   576  						Private: &libkb.MerkleTriple{Seqno: keybase1.Seqno(100)},
   577  					}
   578  				}
   579  			}
   580  			return leaf, root, hiddenResp, err
   581  		},
   582  	}
   583  	m[B].G().SetMerkleClient(corruptMerkle)
   584  	// note that the postProbes we will sample now are different from the ones which we failed on the first time, so we can test we are actually retrying those.
   585  	m[B].G().SetRandom(&MockRandom{t: t, nextOutputs: []int64{int64(firstWithHidden) - 1, int64(firstWithHidden + 1), int64(headMerkleSeqno) + 3, int64(headMerkleSeqno) + 4}})
   586  
   587  	// repeat a second time and make sure that we retry the same probes
   588  	err = auditor.AuditTeam(m[B], teamID, false, team.MainChain().Chain.HeadMerkle.Seqno, team.MainChain().Chain.LinkIDs, team.HiddenChain().GetOuter(), team.MainChain().Chain.LastSeqno, team.HiddenChain().GetLastCommittedSeqno(), root, keybase1.AuditMode_STANDARD)
   589  	require.Error(t, err)
   590  	require.IsType(t, AuditError{}, err)
   591  	require.Zero(t, numProbes, "not all probes were retried")
   592  
   593  	// now test the preprobes are saved and retried on failure
   594  	corruptMerkle = CorruptingMerkleClient{
   595  		MerkleClientInterface: merkle,
   596  		corruptor: func(leaf *libkb.MerkleGenericLeaf, root *libkb.MerkleRoot, hiddenResp *libkb.MerkleHiddenResponse, err error) (*libkb.MerkleGenericLeaf, *libkb.MerkleRoot, *libkb.MerkleHiddenResponse, error) {
   597  			t.Logf("Corruptor: received %v,%v,%v,%v", leaf, root, hiddenResp, err)
   598  			if leaf == nil {
   599  				leaf = &libkb.MerkleGenericLeaf{
   600  					LeafID: teamID.AsUserOrTeam(),
   601  				}
   602  			}
   603  			if leaf.Private == nil {
   604  				t.Logf("Corruptor: creating a fake leaf when there should have been none")
   605  				leaf.Private = &libkb.MerkleTriple{
   606  					Seqno:  4,
   607  					LinkID: []byte{0x00, 0x01, 0x02},
   608  				}
   609  			}
   610  			return leaf, root, hiddenResp, err
   611  		},
   612  	}
   613  	m[B].G().SetMerkleClient(corruptMerkle)
   614  	m[B].G().SetRandom(&MockRandom{t: t, nextOutputs: []int64{int64(firstWithHidden) - 1, int64(firstWithHidden + 1), int64(headMerkleSeqno) + 3, int64(headMerkleSeqno) + 4}})
   615  
   616  	err = auditor.AuditTeam(m[B], teamID, false, team.MainChain().Chain.HeadMerkle.Seqno, team.MainChain().Chain.LinkIDs, team.HiddenChain().GetOuter(), team.MainChain().Chain.LastSeqno, team.HiddenChain().GetLastCommittedSeqno(), root, keybase1.AuditMode_STANDARD)
   617  	require.Error(t, err)
   618  	require.IsType(t, AuditError{}, err)
   619  	require.Contains(t, err.Error(), "merkle root should not have had a leaf for team")
   620  
   621  	history, err = auditor.getFromCache(m[B], teamID, lru)
   622  	require.NoError(t, err)
   623  	require.Len(t, history.PreProbesToRetry, 2)
   624  	require.Contains(t, history.PreProbesToRetry, firstWithHidden-1)
   625  	require.Contains(t, history.PreProbesToRetry, firstWithHidden+1)
   626  
   627  	// the old failed postprobes are still in the cache
   628  	require.Len(t, history.PostProbesToRetry, 2)
   629  	require.Contains(t, history.PostProbesToRetry, headMerkleSeqno+1)
   630  	require.Contains(t, history.PostProbesToRetry, headMerkleSeqno+2)
   631  
   632  	probesToTest = make(map[keybase1.Seqno]bool)
   633  	probesToTestLock.Lock()
   634  	probesToTest[firstWithHidden-1] = true
   635  	probesToTest[firstWithHidden+1] = true
   636  	probesToTestLock.Unlock()
   637  	numProbes = 2
   638  
   639  	corruptMerkle = CorruptingMerkleClient{
   640  		MerkleClientInterface: merkle,
   641  		corruptor: func(leaf *libkb.MerkleGenericLeaf, root *libkb.MerkleRoot, hiddenResp *libkb.MerkleHiddenResponse, err error) (*libkb.MerkleGenericLeaf, *libkb.MerkleRoot, *libkb.MerkleHiddenResponse, error) {
   642  			t.Logf("Corruptor: received %v,%v,%v,%v", leaf, root, hiddenResp, err)
   643  			probeSeqno := *root.Seqno()
   644  			if probeSeqno <= headMerkleSeqno {
   645  				if probesToTest[probeSeqno] {
   646  					t.Logf("PreProbes: Seqno %v was retried", probeSeqno)
   647  					probesToTestLock.Lock()
   648  					probesToTest[probeSeqno] = false
   649  					numProbes--
   650  					probesToTestLock.Unlock()
   651  				}
   652  				if leaf == nil {
   653  					leaf = &libkb.MerkleGenericLeaf{
   654  						LeafID: teamID.AsUserOrTeam(),
   655  					}
   656  				}
   657  				if leaf.Private == nil {
   658  					t.Logf("Corruptor: creating a fake leaf when there should have been none")
   659  					leaf.Private = &libkb.MerkleTriple{
   660  						Seqno:  4,
   661  						LinkID: []byte{0x00, 0x01, 0x02},
   662  					}
   663  				}
   664  			}
   665  			return leaf, root, hiddenResp, err
   666  		},
   667  	}
   668  	m[B].G().SetMerkleClient(corruptMerkle)
   669  	m[B].G().SetRandom(&MockRandom{t: t, nextOutputs: []int64{int64(firstWithHidden) - 2, int64(firstWithHidden) + 2, int64(headMerkleSeqno) + 3, int64(headMerkleSeqno) + 4}})
   670  
   671  	err = auditor.AuditTeam(m[B], teamID, false, team.MainChain().Chain.HeadMerkle.Seqno, team.MainChain().Chain.LinkIDs, team.HiddenChain().GetOuter(), team.MainChain().Chain.LastSeqno, team.HiddenChain().GetLastCommittedSeqno(), root, keybase1.AuditMode_STANDARD)
   672  	require.Error(t, err)
   673  	require.IsType(t, AuditError{}, err)
   674  	require.Zero(t, numProbes, "not all probes were retried")
   675  
   676  	history, err = auditor.getFromCache(m[B], teamID, lru)
   677  	require.NoError(t, err)
   678  	require.Len(t, history.PreProbesToRetry, 2)
   679  	require.Contains(t, history.PreProbesToRetry, firstWithHidden-1)
   680  	require.Contains(t, history.PreProbesToRetry, firstWithHidden+1)
   681  
   682  	// the old failed postprobes are still in the cache
   683  	require.Len(t, history.PostProbesToRetry, 2)
   684  	require.Contains(t, history.PostProbesToRetry, headMerkleSeqno+1)
   685  	require.Contains(t, history.PostProbesToRetry, headMerkleSeqno+2)
   686  
   687  	// now stop any corruption and ensure the audit succeeds
   688  	m[B].G().SetMerkleClient(merkle)
   689  	m[B].G().SetRandom(rand)
   690  	err = auditor.AuditTeam(m[B], teamID, false, team.MainChain().Chain.HeadMerkle.Seqno, team.MainChain().Chain.LinkIDs, team.HiddenChain().GetOuter(), team.MainChain().Chain.LastSeqno, team.HiddenChain().GetLastCommittedSeqno(), root, keybase1.AuditMode_STANDARD)
   691  	require.NoError(t, err)
   692  }