github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/engine/wot_test.go (about)

     1  package engine
     2  
     3  import (
     4  	"fmt"
     5  	"testing"
     6  
     7  	"github.com/keybase/client/go/libkb"
     8  	"github.com/keybase/client/go/protocol/keybase1"
     9  	"github.com/stretchr/testify/require"
    10  )
    11  
    12  func TestWebOfTrustVouch(t *testing.T) {
    13  	tc1 := SetupEngineTest(t, "wot")
    14  	tc2 := SetupEngineTest(t, "wot")
    15  	tc3 := SetupEngineTest(t, "wot")
    16  	defer tc1.Cleanup()
    17  	defer tc2.Cleanup()
    18  	defer tc3.Cleanup()
    19  
    20  	fu1 := CreateAndSignupFakeUser(tc1, "wot")
    21  	fu2 := CreateAndSignupFakeUser(tc2, "wot")
    22  	fu3 := CreateAndSignupFakeUser(tc3, "wot")
    23  
    24  	// make mutual track b/w fu1 and fu2, fu1 and fu3
    25  	sigVersion := libkb.GetDefaultSigVersion(tc1.G)
    26  	trackUser(tc2, fu2, fu1.NormalizedUsername(), sigVersion)
    27  	trackUser(tc1, fu1, fu2.NormalizedUsername(), sigVersion)
    28  	trackUser(tc1, fu1, fu3.NormalizedUsername(), sigVersion)
    29  	trackUser(tc3, fu3, fu1.NormalizedUsername(), sigVersion)
    30  
    31  	err := fu2.LoadUser(tc2)
    32  	require.NoError(tc2.T, err)
    33  
    34  	err = fu1.LoadUser(tc1)
    35  	require.NoError(tc1.T, err)
    36  	idt := fu1.User.IDTable()
    37  	lenBefore := idt.Len()
    38  	// should be logged in as fu1, double check:
    39  	require.Equal(t, tc1.G.ActiveDevice.UID(), fu1.UID())
    40  	mctx := NewMetaContextForTest(tc1)
    41  
    42  	// fu1 vouches for fu2
    43  	arg := &WotVouchArg{
    44  		Vouchee:    fu2.User.ToUserVersion(),
    45  		Confidence: keybase1.Confidence{UsernameVerifiedVia: keybase1.UsernameVerificationType_OTHER_CHAT},
    46  		VouchText:  "alice is awesome",
    47  	}
    48  
    49  	eng := NewWotVouch(tc1.G, arg)
    50  	err = RunEngine2(mctx, eng)
    51  	require.NoError(t, err)
    52  
    53  	err = fu1.LoadUser(tc1)
    54  	require.NoError(tc1.T, err)
    55  	idt = fu1.User.IDTable()
    56  
    57  	// for now, let's just check that it got bigger:
    58  	require.Equal(tc1.T, lenBefore+1, idt.Len())
    59  
    60  	err = fu3.LoadUser(tc3)
    61  	require.NoError(tc3.T, err)
    62  
    63  	// make sure that if the user is attesting to something about
    64  	// a user and eldest seqno changes, that they get an error.
    65  	uv := fu3.User.ToUserVersion()
    66  	uv.EldestSeqno++
    67  	arg = &WotVouchArg{
    68  		Vouchee:    uv,
    69  		Confidence: keybase1.Confidence{UsernameVerifiedVia: keybase1.UsernameVerificationType_OTHER_CHAT},
    70  		VouchText:  "bob is nice",
    71  	}
    72  	eng = NewWotVouch(tc1.G, arg)
    73  	err = RunEngine2(mctx, eng)
    74  	require.Error(tc1.T, err)
    75  
    76  	err = fu1.LoadUser(tc1)
    77  	require.NoError(tc1.T, err)
    78  	idt = fu1.User.IDTable()
    79  	require.Equal(tc1.T, lenBefore+1, idt.Len())
    80  
    81  	// make an fu1 -> fu3 attest with confidence stuff
    82  	arg = &WotVouchArg{
    83  		Vouchee:    fu3.User.ToUserVersion(),
    84  		VouchText:  "charlie rocks",
    85  		Confidence: confidence,
    86  	}
    87  	eng = NewWotVouch(tc1.G, arg)
    88  	err = RunEngine2(mctx, eng)
    89  	require.NoError(tc1.T, err)
    90  
    91  	err = fu1.LoadUser(tc1)
    92  	require.NoError(tc1.T, err)
    93  	idt = fu1.User.IDTable()
    94  	require.Equal(tc1.T, lenBefore+2, idt.Len())
    95  }
    96  
    97  func TestWebOfTrustPending(t *testing.T) {
    98  	tcAlice := SetupEngineTest(t, "wot")
    99  	tcBob := SetupEngineTest(t, "wot")
   100  	defer tcAlice.Cleanup()
   101  	defer tcBob.Cleanup()
   102  	alice := CreateAndSignupFakeUser(tcAlice, "wot")
   103  	bob := CreateAndSignupFakeUser(tcBob, "wot")
   104  	mctxA := NewMetaContextForTest(tcAlice)
   105  	mctxB := NewMetaContextForTest(tcBob)
   106  	t.Log("alice and bob exist")
   107  
   108  	sigVersion := libkb.GetDefaultSigVersion(tcAlice.G)
   109  	trackUser(tcBob, bob, alice.NormalizedUsername(), sigVersion)
   110  	trackUser(tcAlice, alice, bob.NormalizedUsername(), sigVersion)
   111  	err := bob.LoadUser(tcBob)
   112  	require.NoError(tcBob.T, err)
   113  	err = alice.LoadUser(tcAlice)
   114  	require.NoError(tcAlice.T, err)
   115  	t.Log("alice and bob follow each other")
   116  
   117  	aliceName := alice.User.GetName()
   118  	bobName := bob.User.GetName()
   119  
   120  	var vouches []keybase1.WotVouch
   121  	vouches, err = libkb.FetchWotVouches(mctxA, libkb.FetchWotVouchesArg{})
   122  	require.NoError(t, err)
   123  	require.Empty(t, vouches)
   124  	t.Log("alice has no pending vouches")
   125  	vouches, err = libkb.FetchWotVouches(mctxB, libkb.FetchWotVouchesArg{Vouchee: aliceName})
   126  	require.NoError(t, err)
   127  	require.Empty(t, vouches)
   128  	t.Log("bob sees no vouches for Alice")
   129  
   130  	firstVouch := "alice is wondibar but i don't have much confidence"
   131  	arg := &WotVouchArg{
   132  		Vouchee:    alice.User.ToUserVersion(),
   133  		Confidence: keybase1.Confidence{UsernameVerifiedVia: keybase1.UsernameVerificationType_OTHER_CHAT},
   134  		VouchText:  firstVouch,
   135  	}
   136  	eng := NewWotVouch(tcBob.G, arg)
   137  	err = RunEngine2(mctxB, eng)
   138  	require.NoError(t, err)
   139  	t.Log("bob vouches for alice without confidence")
   140  
   141  	vouches, err = libkb.FetchWotVouches(mctxA, libkb.FetchWotVouchesArg{})
   142  	require.NoError(t, err)
   143  	require.Len(t, vouches, 1)
   144  	bobVouch := vouches[0]
   145  	require.Equal(t, bob.User.GetUID(), bobVouch.Voucher.Uid)
   146  	require.Equal(t, bobName, bobVouch.VoucherUsername)
   147  	require.Equal(t, aliceName, bobVouch.VoucheeUsername)
   148  	require.Equal(t, firstVouch, bobVouch.VouchText)
   149  	require.NotNil(t, bobVouch.Confidence)
   150  	require.EqualValues(t, keybase1.UsernameVerificationType_OTHER_CHAT, bobVouch.Confidence.UsernameVerifiedVia)
   151  	require.Equal(t, keybase1.WotStatusType_PROPOSED, bobVouch.Status)
   152  	t.Log("alice sees one pending vouch")
   153  	vouches, err = libkb.FetchWotVouches(mctxB, libkb.FetchWotVouchesArg{Vouchee: aliceName})
   154  	require.NoError(t, err)
   155  	require.Equal(t, 1, len(vouches))
   156  	t.Log("bob also sees his pending vouch for Alice")
   157  
   158  	tcCharlie := SetupEngineTest(t, "wot")
   159  	defer tcCharlie.Cleanup()
   160  	charlie := CreateAndSignupFakeUser(tcCharlie, "wot")
   161  	mctxC := NewMetaContextForTest(tcCharlie)
   162  	t.Log("charlie exists")
   163  
   164  	trackUser(tcCharlie, charlie, alice.NormalizedUsername(), sigVersion)
   165  	trackUser(tcAlice, alice, charlie.NormalizedUsername(), sigVersion)
   166  	err = charlie.LoadUser(tcCharlie)
   167  	require.NoError(tcCharlie.T, err)
   168  	t.Log("alice and charlie follow each other")
   169  
   170  	charlieName := charlie.User.GetName()
   171  
   172  	vouchText := "alice is wondibar and doug agrees"
   173  	arg = &WotVouchArg{
   174  		Vouchee:    alice.User.ToUserVersion(),
   175  		VouchText:  vouchText,
   176  		Confidence: confidence,
   177  	}
   178  	eng = NewWotVouch(tcCharlie.G, arg)
   179  	err = RunEngine2(mctxC, eng)
   180  	require.NoError(t, err)
   181  	t.Log("charlie vouches for alice with confidence")
   182  
   183  	// ensure alice does a full load of bob by adding another link
   184  	// to bob's chain so the wot.vouch isn't the last one (which is always unstubbed)
   185  	// and nuking alice's local db to wipe any cache
   186  	trackUser(tcBob, bob, charlie.NormalizedUsername(), sigVersion)
   187  	_, err = mctxA.G().LocalDb.Nuke()
   188  	require.NoError(t, err)
   189  
   190  	vouches, err = libkb.FetchWotVouches(mctxA, libkb.FetchWotVouchesArg{})
   191  	require.NoError(t, err)
   192  	require.Len(t, vouches, 2)
   193  	require.EqualValues(t, bobVouch, vouches[0])
   194  	charlieVouch := vouches[1]
   195  	require.Equal(t, keybase1.WotStatusType_PROPOSED, charlieVouch.Status)
   196  	require.Equal(t, confidence, charlieVouch.Confidence)
   197  	t.Log("alice sees two pending vouches")
   198  
   199  	// alice gets just charlie's vouch using FetchWotVouches
   200  	vouches, err = libkb.FetchWotVouches(mctxA, libkb.FetchWotVouchesArg{Vouchee: aliceName, Voucher: charlieName})
   201  	require.NoError(t, err)
   202  	require.Len(t, vouches, 1)
   203  	require.Equal(t, keybase1.WotStatusType_PROPOSED, vouches[0].Status)
   204  	t.Log("alice sees charlie's pending vouch")
   205  }
   206  
   207  func TestWebOfTrustAccept(t *testing.T) {
   208  	tcAlice := SetupEngineTest(t, "wot")
   209  	tcBob := SetupEngineTest(t, "wot")
   210  	defer tcAlice.Cleanup()
   211  	defer tcBob.Cleanup()
   212  	alice := CreateAndSignupFakeUser(tcAlice, "wot")
   213  	bob := CreateAndSignupFakeUser(tcBob, "wot")
   214  	mctxA := NewMetaContextForTest(tcAlice)
   215  	mctxB := NewMetaContextForTest(tcBob)
   216  	t.Log("alice and bob exist")
   217  
   218  	sigVersion := libkb.GetDefaultSigVersion(tcAlice.G)
   219  	trackUser(tcBob, bob, alice.NormalizedUsername(), sigVersion)
   220  	trackUser(tcAlice, alice, bob.NormalizedUsername(), sigVersion)
   221  	err := bob.LoadUser(tcBob)
   222  	require.NoError(tcBob.T, err)
   223  	err = alice.LoadUser(tcAlice)
   224  	require.NoError(tcAlice.T, err)
   225  	t.Log("alice and bob follow each other")
   226  
   227  	aliceName := alice.User.GetName()
   228  	bobName := bob.User.GetName()
   229  
   230  	vouchText := "alice is wondibar and doug agrees"
   231  	argV := &WotVouchArg{
   232  		Vouchee:    alice.User.ToUserVersion(),
   233  		VouchText:  vouchText,
   234  		Confidence: confidence,
   235  	}
   236  	engV := NewWotVouch(tcBob.G, argV)
   237  	err = RunEngine2(mctxB, engV)
   238  	require.NoError(t, err)
   239  	t.Log("bob vouches for alice with confidence")
   240  
   241  	vouches, err := libkb.FetchWotVouches(mctxA, libkb.FetchWotVouchesArg{})
   242  	require.NoError(t, err)
   243  	require.Len(t, vouches, 1)
   244  	bobVouch := vouches[0]
   245  	require.Equal(t, keybase1.WotStatusType_PROPOSED, bobVouch.Status)
   246  	require.Equal(t, bob.User.GetUID(), bobVouch.Voucher.Uid)
   247  	require.Equal(t, bobName, bobVouch.VoucherUsername)
   248  	require.Equal(t, aliceName, bobVouch.VoucheeUsername)
   249  	require.Equal(t, vouchText, bobVouch.VouchText)
   250  	t.Log("alice fetches one pending vouch")
   251  
   252  	argR := &WotReactArg{
   253  		Voucher:  bob.User.ToUserVersion(),
   254  		Proof:    bobVouch.VouchProof,
   255  		Reaction: keybase1.WotReactionType_ACCEPT,
   256  	}
   257  	engR := NewWotReact(tcAlice.G, argR)
   258  	err = RunEngine2(mctxA, engR)
   259  	require.NoError(t, err)
   260  	t.Log("alice accepts")
   261  
   262  	vouches, err = libkb.FetchWotVouches(mctxA, libkb.FetchWotVouchesArg{})
   263  	require.NoError(t, err)
   264  	require.Equal(t, 1, len(vouches))
   265  	vouch := vouches[0]
   266  	require.Equal(t, keybase1.WotStatusType_ACCEPTED, vouch.Status)
   267  	require.Equal(t, bob.User.GetUID(), vouch.Voucher.Uid)
   268  	require.Equal(t, vouchText, vouch.VouchText)
   269  	require.EqualValues(t, confidence, vouch.Confidence)
   270  
   271  	vouches, err = libkb.FetchWotVouches(mctxB, libkb.FetchWotVouchesArg{Vouchee: aliceName})
   272  	require.NoError(t, err)
   273  	require.Equal(t, 1, len(vouches))
   274  	vouch = vouches[0]
   275  	require.Equal(t, keybase1.WotStatusType_ACCEPTED, vouch.Status)
   276  	require.Equal(t, bob.User.GetUID(), vouch.Voucher.Uid)
   277  	require.Equal(t, vouchText, vouch.VouchText)
   278  	require.EqualValues(t, confidence, vouch.Confidence)
   279  }
   280  
   281  func TestWebOfTrustReject(t *testing.T) {
   282  	tcAlice := SetupEngineTest(t, "wot")
   283  	tcBob := SetupEngineTest(t, "wot")
   284  	defer tcAlice.Cleanup()
   285  	defer tcBob.Cleanup()
   286  	alice := CreateAndSignupFakeUser(tcAlice, "wot")
   287  	bob := CreateAndSignupFakeUser(tcBob, "wot")
   288  	mctxA := NewMetaContextForTest(tcAlice)
   289  	mctxB := NewMetaContextForTest(tcBob)
   290  	t.Log("alice and bob exist")
   291  
   292  	sigVersion := libkb.GetDefaultSigVersion(tcAlice.G)
   293  	trackUser(tcBob, bob, alice.NormalizedUsername(), sigVersion)
   294  	trackUser(tcAlice, alice, bob.NormalizedUsername(), sigVersion)
   295  	err := bob.LoadUser(tcBob)
   296  	require.NoError(tcBob.T, err)
   297  	err = alice.LoadUser(tcAlice)
   298  	require.NoError(tcAlice.T, err)
   299  	t.Log("alice and bob follow each other")
   300  
   301  	aliceName := alice.User.GetName()
   302  
   303  	vouchText := "alice is wondibar"
   304  	argV := &WotVouchArg{
   305  		Vouchee:    alice.User.ToUserVersion(),
   306  		Confidence: keybase1.Confidence{UsernameVerifiedVia: keybase1.UsernameVerificationType_OTHER_CHAT},
   307  		VouchText:  vouchText,
   308  	}
   309  	engV := NewWotVouch(tcBob.G, argV)
   310  	err = RunEngine2(mctxB, engV)
   311  	require.NoError(t, err)
   312  	t.Log("bob vouches for alice")
   313  
   314  	vouches, err := libkb.FetchWotVouches(mctxA, libkb.FetchWotVouchesArg{})
   315  	require.NoError(t, err)
   316  	require.Len(t, vouches, 1)
   317  	bobVouch := vouches[0]
   318  	require.Equal(t, keybase1.WotStatusType_PROPOSED, bobVouch.Status)
   319  	require.Equal(t, bob.User.GetUID(), bobVouch.Voucher.Uid)
   320  	require.Equal(t, vouchText, bobVouch.VouchText)
   321  	t.Log("alice fetches one pending vouch")
   322  
   323  	argR := &WotReactArg{
   324  		Voucher:  bob.User.ToUserVersion(),
   325  		Proof:    bobVouch.VouchProof,
   326  		Reaction: keybase1.WotReactionType_REJECT,
   327  	}
   328  	engR := NewWotReact(tcAlice.G, argR)
   329  	err = RunEngine2(mctxA, engR)
   330  	require.NoError(t, err)
   331  	t.Log("alice rejects it")
   332  
   333  	vouches, err = libkb.FetchWotVouches(mctxA, libkb.FetchWotVouchesArg{})
   334  	require.NoError(t, err)
   335  	require.Equal(t, 1, len(vouches))
   336  	vouch := vouches[0]
   337  	require.Equal(t, keybase1.WotStatusType_REJECTED, vouch.Status)
   338  	require.Equal(t, bob.User.GetUID(), vouch.Voucher.Uid)
   339  	require.Equal(t, vouchText, vouch.VouchText)
   340  	require.NotNil(t, vouch.Confidence)
   341  	require.EqualValues(t, keybase1.UsernameVerificationType_OTHER_CHAT, bobVouch.Confidence.UsernameVerifiedVia)
   342  	t.Log("alice can see it as rejected")
   343  
   344  	vouches, err = libkb.FetchWotVouches(mctxB, libkb.FetchWotVouchesArg{Vouchee: aliceName})
   345  	require.NoError(t, err)
   346  	require.Equal(t, 1, len(vouches))
   347  	require.Equal(t, keybase1.WotStatusType_REJECTED, vouches[0].Status)
   348  	t.Log("bob can also see it as rejected")
   349  }
   350  
   351  func TestWebOfTrustRevoke(t *testing.T) {
   352  	var err error
   353  	tcAlice := SetupEngineTest(t, "wot")
   354  	defer tcAlice.Cleanup()
   355  	tcBob := SetupEngineTest(t, "wot")
   356  	defer tcBob.Cleanup()
   357  	alice := CreateAndSignupFakeUser(tcAlice, "wot")
   358  	uisA := libkb.UIs{
   359  		LogUI:    tcAlice.G.UI.GetLogUI(),
   360  		SecretUI: alice.NewSecretUI(),
   361  	}
   362  	mctxA := NewMetaContextForTest(tcAlice).WithUIs(uisA)
   363  	bob := CreateAndSignupFakeUser(tcBob, "wot")
   364  	uisB := libkb.UIs{
   365  		LogUI:    tcBob.G.UI.GetLogUI(),
   366  		SecretUI: bob.NewSecretUI(),
   367  	}
   368  	mctxB := NewMetaContextForTest(tcBob).WithUIs(uisB)
   369  	aliceName := alice.NormalizedUsername().String()
   370  	bobName := bob.NormalizedUsername().String()
   371  	t.Logf("alice: %s and bob: %s exist", aliceName, bobName)
   372  	sigVersion := libkb.GetDefaultSigVersion(tcAlice.G)
   373  	trackUser(tcBob, bob, alice.NormalizedUsername(), sigVersion)
   374  	trackUser(tcAlice, alice, bob.NormalizedUsername(), sigVersion)
   375  	err = bob.LoadUser(tcBob)
   376  	require.NoError(tcBob.T, err)
   377  	err = alice.LoadUser(tcAlice)
   378  	require.NoError(tcAlice.T, err)
   379  	t.Log("alice and bob follow each other")
   380  
   381  	bobVouchesForAlice := func(version int) {
   382  		vouchText := fmt.Sprintf("alice is wondibar v%d", version)
   383  		arg := &WotVouchArg{
   384  			Vouchee:    alice.User.ToUserVersion(),
   385  			VouchText:  vouchText,
   386  			Confidence: confidence,
   387  		}
   388  		eng := NewWotVouch(tcBob.G, arg)
   389  		err = RunEngine2(mctxB, eng)
   390  		require.NoError(t, err)
   391  	}
   392  	aliceAccepts := func(sigID keybase1.SigID) error {
   393  		arg := &WotReactArg{
   394  			Voucher:  bob.User.ToUserVersion(),
   395  			Proof:    sigID,
   396  			Reaction: keybase1.WotReactionType_ACCEPT,
   397  		}
   398  		eng := NewWotReact(tcAlice.G, arg)
   399  		return RunEngine2(mctxA, eng)
   400  	}
   401  	aliceRejects := func(sigID keybase1.SigID) error {
   402  		arg := &WotReactArg{
   403  			Voucher:  bob.User.ToUserVersion(),
   404  			Proof:    sigID,
   405  			Reaction: keybase1.WotReactionType_REJECT,
   406  		}
   407  		eng := NewWotReact(tcAlice.G, arg)
   408  		return RunEngine2(mctxA, eng)
   409  	}
   410  	assertFetch := func(mctx libkb.MetaContext, version int, expectedStatus keybase1.WotStatusType) keybase1.WotVouch {
   411  		vouches, err := libkb.FetchWotVouches(mctx, libkb.FetchWotVouchesArg{Voucher: bobName, Vouchee: aliceName})
   412  		require.NoError(t, err)
   413  		require.Equal(t, 1, len(vouches))
   414  		vouch := vouches[0]
   415  		require.Equal(t, expectedStatus, vouch.Status)
   416  		require.Equal(t, bob.User.GetUID(), vouch.Voucher.Uid)
   417  		require.Equal(t, alice.User.GetUID(), vouch.Vouchee.Uid)
   418  		expectedVouchText := fmt.Sprintf("alice is wondibar v%d", version)
   419  		require.Equal(t, expectedVouchText, vouch.VouchText)
   420  		require.NotNil(t, vouch.Confidence)
   421  		return vouch
   422  	}
   423  	revokeSig := func(mctx libkb.MetaContext, sigID string) {
   424  		eng := NewRevokeSigsEngine(mctx.G(), []string{sigID})
   425  		err := RunEngine2(mctx, eng)
   426  		require.NoError(t, err)
   427  	}
   428  
   429  	// bob vouches for alice
   430  	vouchVersion := 1
   431  	bobVouchesForAlice(vouchVersion)
   432  	_ = assertFetch(mctxA, vouchVersion, keybase1.WotStatusType_PROPOSED)
   433  	wotVouchOne := assertFetch(mctxB, vouchVersion, keybase1.WotStatusType_PROPOSED)
   434  	t.Log("bob vouches for alice and everything looks good")
   435  
   436  	// bob revokes the attestation
   437  	revokeSig(mctxB, wotVouchOne.VouchProof.String())
   438  	_ = assertFetch(mctxA, vouchVersion, keybase1.WotStatusType_REVOKED)
   439  	_ = assertFetch(mctxB, vouchVersion, keybase1.WotStatusType_REVOKED)
   440  	t.Log("bob revokes the chainlink with the attestation, and it comes back `revoked` for both of them")
   441  
   442  	// bob vouches again
   443  	vouchVersion++
   444  	bobVouchesForAlice(vouchVersion)
   445  	_ = assertFetch(mctxA, vouchVersion, keybase1.WotStatusType_PROPOSED)
   446  	wotVouchTwo := assertFetch(mctxB, vouchVersion, keybase1.WotStatusType_PROPOSED)
   447  
   448  	// alice cannot accept the revoked proof
   449  	err = aliceAccepts(wotVouchOne.VouchProof)
   450  	require.Error(t, err)
   451  	// alice accepts the new proposed proof
   452  	err = aliceAccepts(wotVouchTwo.VouchProof)
   453  	require.NoError(t, err)
   454  	_ = assertFetch(mctxA, vouchVersion, keybase1.WotStatusType_ACCEPTED)
   455  	_ = assertFetch(mctxB, vouchVersion, keybase1.WotStatusType_ACCEPTED)
   456  
   457  	// alice revokes her acceptance
   458  	err = alice.LoadUser(tcAlice)
   459  	require.NoError(t, err)
   460  	aliceLastLink := alice.User.GetLastLink()
   461  	tlink, w := libkb.NewTypedChainLink(aliceLastLink)
   462  	require.Nil(t, w)
   463  	reactionLink, ok := tlink.(*libkb.WotReactChainLink)
   464  	require.True(t, ok)
   465  	revokeSig(mctxA, reactionLink.GetSigID().String())
   466  	// it goes back to proposed
   467  	_ = assertFetch(mctxA, vouchVersion, keybase1.WotStatusType_PROPOSED)
   468  	_ = assertFetch(mctxB, vouchVersion, keybase1.WotStatusType_PROPOSED)
   469  	// and now she accepts it and it looks accepted to both of them
   470  	err = aliceAccepts(wotVouchTwo.VouchProof)
   471  	require.NoError(t, err)
   472  	_ = assertFetch(mctxA, vouchVersion, keybase1.WotStatusType_ACCEPTED)
   473  	_ = assertFetch(mctxB, vouchVersion, keybase1.WotStatusType_ACCEPTED)
   474  
   475  	// bob revokes and it shows up as revoked
   476  	revokeSig(mctxB, wotVouchTwo.VouchProof.String())
   477  	_ = assertFetch(mctxA, vouchVersion, keybase1.WotStatusType_REVOKED)
   478  	_ = assertFetch(mctxB, vouchVersion, keybase1.WotStatusType_REVOKED)
   479  
   480  	///////////
   481  	// VouchWithRevoke
   482  	///////////
   483  	assertUserLastLinkIsVouchWithRevoke := func(user *FakeUser, tc libkb.TestContext) *libkb.WotVouchChainLink {
   484  		err := user.LoadUser(tc)
   485  		require.NoError(t, err)
   486  		lastLink := user.User.GetLastLink()
   487  		tlink, warning := libkb.NewTypedChainLink(lastLink)
   488  		require.Nil(t, warning)
   489  		vouchLink, ok := tlink.(*libkb.WotVouchChainLink)
   490  		require.True(t, ok)
   491  		require.Equal(t, 1, len(vouchLink.Revocations))
   492  		return vouchLink
   493  	}
   494  	vouchVersion++
   495  	bobVouchesForAlice(vouchVersion)
   496  	_ = assertFetch(mctxA, vouchVersion, keybase1.WotStatusType_PROPOSED)
   497  	vouchToRevoke := assertFetch(mctxB, vouchVersion, keybase1.WotStatusType_PROPOSED)
   498  	// another vouch on top of an unrevoked previous one should be of type vouchWithRevoke
   499  	vouchVersion++
   500  	bobVouchesForAlice(vouchVersion)
   501  	_ = assertFetch(mctxA, vouchVersion, keybase1.WotStatusType_PROPOSED)
   502  	vouchToAccept := assertFetch(mctxB, vouchVersion, keybase1.WotStatusType_PROPOSED)
   503  	vwrLink := assertUserLastLinkIsVouchWithRevoke(bob, tcBob)
   504  	require.Contains(t, vwrLink.Revocations, vouchToRevoke.VouchProof)
   505  	err = aliceAccepts(vouchToRevoke.VouchProof)
   506  	require.Error(t, err)
   507  
   508  	// can vouchWithRevoke an accepted vouch, and then accept it
   509  	err = aliceAccepts(vouchToAccept.VouchProof)
   510  	vouchToRevoke = vouchToAccept
   511  	require.NoError(t, err)
   512  	vouchVersion++
   513  	bobVouchesForAlice(vouchVersion)
   514  	vouchToAccept = assertFetch(mctxA, vouchVersion, keybase1.WotStatusType_PROPOSED)
   515  	_ = assertFetch(mctxB, vouchVersion, keybase1.WotStatusType_PROPOSED)
   516  	vwrLink = assertUserLastLinkIsVouchWithRevoke(bob, tcBob)
   517  	require.Contains(t, vwrLink.Revocations, vouchToRevoke.VouchProof)
   518  	err = aliceAccepts(vouchToAccept.VouchProof)
   519  	require.NoError(t, err)
   520  	// it is significant that bob has not loaded alice between these two steps
   521  	// see comment in sig_chain.go#GetLinkFromSigID
   522  	err = aliceRejects(vouchToAccept.VouchProof)
   523  	require.NoError(t, err)
   524  
   525  	// can vouchWithRevoke a rejected vouch, and then accept it
   526  	vouchToRevoke = assertFetch(mctxB, vouchVersion, keybase1.WotStatusType_REJECTED)
   527  	bobVouchesForAlice(vouchVersion)
   528  	_ = assertFetch(mctxA, vouchVersion, keybase1.WotStatusType_PROPOSED)
   529  	vouchToAccept = assertFetch(mctxB, vouchVersion, keybase1.WotStatusType_PROPOSED)
   530  	vwrLink = assertUserLastLinkIsVouchWithRevoke(bob, tcBob)
   531  	require.Contains(t, vwrLink.Revocations, vouchToRevoke.VouchProof)
   532  	err = aliceAccepts(vouchToAccept.VouchProof)
   533  	require.NoError(t, err)
   534  
   535  	// confirm that vouch and vouchWithRevoke links are not stubbed by the server
   536  	// by checking that, when they are not the last link in someone's chain (which is always unstubbed),
   537  	// an unstubbed load is successful, and subsequently, those vouches are reactable.
   538  	// ------------------------------------------
   539  	// create a new user
   540  	tcCharlie := SetupEngineTest(t, "wot")
   541  	defer tcCharlie.Cleanup()
   542  	charlie := CreateAndSignupFakeUser(tcCharlie, "wot")
   543  	// clear out so our next vouch link doesn't have a revoke
   544  	revokeSig(mctxB, vouchToAccept.VouchProof.String())
   545  
   546  	assertVouchLinkLoadsCleanly := func(vouchToAccept keybase1.WotVouch) {
   547  		// add two stubbable links to the end of bob's chain
   548  		trackUser(tcBob, bob, charlie.NormalizedUsername(), sigVersion)
   549  		require.NoError(t, runUntrack(tcBob, bob, charlie.NormalizedUsername().String(), sigVersion))
   550  		// wipe alice so the load is definitely uncached
   551  		_, err := mctxA.G().LocalDb.Nuke()
   552  		require.NoError(t, err)
   553  		// alice can do a normal stubbed load of bob (there are no problems with vouch links being stubbed)
   554  		_, err = libkb.LoadUser(libkb.NewLoadUserByNameArg(tcAlice.G, bobName))
   555  		require.NoError(t, err)
   556  		// alice can still react to this link after having done a stubbed load
   557  		err = aliceAccepts(vouchToAccept.VouchProof)
   558  		require.NoError(t, err)
   559  	}
   560  	// a vouch link buried inside a sigchain does not cause loading/stubbing issues
   561  	vouchVersion++
   562  	bobVouchesForAlice(vouchVersion)
   563  	vouchToAccept = assertFetch(mctxB, vouchVersion, keybase1.WotStatusType_PROPOSED)
   564  	assertVouchLinkLoadsCleanly(vouchToAccept)
   565  	// a vouch_with_revoke link buried inside a sigchain does not cause loading/stubbing issues
   566  	vouchVersion++
   567  	bobVouchesForAlice(vouchVersion)
   568  	_ = assertUserLastLinkIsVouchWithRevoke(bob, tcBob)
   569  	vouchToAccept = assertFetch(mctxB, vouchVersion, keybase1.WotStatusType_PROPOSED)
   570  	assertVouchLinkLoadsCleanly(vouchToAccept)
   571  }
   572  
   573  // perhaps revisit after Y2K-1494
   574  func TestWebOfTrustSigBug(t *testing.T) {
   575  	tcAlice := SetupEngineTest(t, "wot")
   576  	tcBob := SetupEngineTest(t, "wot")
   577  	defer tcAlice.Cleanup()
   578  	defer tcBob.Cleanup()
   579  	alice := CreateAndSignupFakeUser(tcAlice, "wot")
   580  	bob := CreateAndSignupFakeUser(tcBob, "wot")
   581  	mctxA := NewMetaContextForTest(tcAlice)
   582  	mctxB := NewMetaContextForTest(tcBob)
   583  	t.Log("alice and bob exist")
   584  
   585  	sigVersion := libkb.GetDefaultSigVersion(tcAlice.G)
   586  	trackUser(tcBob, bob, alice.NormalizedUsername(), sigVersion)
   587  	trackUser(tcAlice, alice, bob.NormalizedUsername(), sigVersion)
   588  	err := bob.LoadUser(tcBob)
   589  	require.NoError(tcBob.T, err)
   590  	err = alice.LoadUser(tcAlice)
   591  	require.NoError(tcAlice.T, err)
   592  	t.Log("alice and bob follow each other")
   593  
   594  	// bob vouches for alice
   595  	firstVouch := "alice is wondibar cause we texted"
   596  	argV := &WotVouchArg{
   597  		Vouchee:    alice.User.ToUserVersion(),
   598  		Confidence: keybase1.Confidence{UsernameVerifiedVia: keybase1.UsernameVerificationType_OTHER_CHAT},
   599  		VouchText:  firstVouch,
   600  	}
   601  	engV := NewWotVouch(tcBob.G, argV)
   602  	err = RunEngine2(mctxB, engV)
   603  	require.NoError(t, err)
   604  
   605  	// alice rejects
   606  	vouches, err := libkb.FetchWotVouches(mctxA, libkb.FetchWotVouchesArg{})
   607  	require.NoError(t, err)
   608  	require.Len(t, vouches, 1)
   609  	bobVouch := vouches[0]
   610  	argR := &WotReactArg{
   611  		Voucher:  bob.User.ToUserVersion(),
   612  		Proof:    bobVouch.VouchProof,
   613  		Reaction: keybase1.WotReactionType_REJECT,
   614  	}
   615  	engR := NewWotReact(tcAlice.G, argR)
   616  	err = RunEngine2(mctxA, engR)
   617  	require.NoError(t, err)
   618  	t.Log("alice rejects it")
   619  	_, err = mctxA.G().LocalDb.Nuke()
   620  	require.NoError(t, err)
   621  
   622  	// bob vouches for alice
   623  	engV2 := NewWotVouch(tcBob.G, argV)
   624  	err = RunEngine2(mctxB, engV2)
   625  	require.NoError(t, err)
   626  
   627  	// this attestation is correctly recognized as proposed
   628  	vouches, err = libkb.FetchWotVouches(mctxA, libkb.FetchWotVouchesArg{})
   629  	require.NoError(t, err)
   630  	bobVouch = vouches[0]
   631  	require.Equal(t, bobVouch.Status, keybase1.WotStatusType_PROPOSED)
   632  }
   633  
   634  var confidence = keybase1.Confidence{
   635  	UsernameVerifiedVia: keybase1.UsernameVerificationType_PROOFS,
   636  	Proofs: []keybase1.WotProof{
   637  		{ProofType: keybase1.ProofType_REDDIT, Name: "reddit", Username: "betaveros"},
   638  		{ProofType: keybase1.ProofType_GENERIC_SOCIAL, Name: "mastodon.social", Username: "gammaveros"},
   639  		{ProofType: keybase1.ProofType_GENERIC_WEB_SITE, Protocol: "https:", Username: "beta.veros"},
   640  		{ProofType: keybase1.ProofType_DNS, Protocol: "dns", Username: "beta.veros"},
   641  	},
   642  }