You are an expert in writing code in many different languages. Your goal is to perform code completion for the following code, keeping in mind the rest of the code and the file meta data.
    Metadata details:
    filename: build.go
    path of file: ./repos/client/go/stellar
    
    the code of the file until where you have to start completion: package stellar

import (
	"fmt"
	"regexp"
	"strings"
	"sync"
	"time"

	"github.com/keybase/client/go/stellar/remote"

	"github.com/keybase/client/go/engine"
	"github.com/keybase/client/go/externals"
	"github.com/keybase/client/go/libkb"
	"github.com/keybase/client/go/protocol/keybase1"
	"github.com/keybase/client/go/protocol/stellar1"
	"github.com/keybase/client/go/slotctx"
	"github.com/keybase/client/go/stellar/stellarcommon"
	"github
    
func ReviewPaymentLocal(mctx libkb.MetaContext, stellarUI stellar1.UiInterface, arg stellar1.ReviewPaymentLocalArg) (err error) {
	tracer := mctx.G().CTimeTracer(mctx.Ctx(), "ReviewPaymentLocal", true)
	defer tracer.Finish()

	if arg.Bid.IsNil() {
		return fmt.Errorf("missing payment ID")
	}

	mctx, data, release, err := getGlobal(mctx.G()).acquireBuildPayment(mctx, arg.Bid, arg.SessionID)
	defer release()
	if err != nil {
		return err
	}

	seqno := 0
	notify := func(banners []stellar1.SendBannerLocal, nextButton reviewButtonState) chan struct{} {
		seqno++
		seqno := seqno                    // Shadow seqno to freeze it for the goroutine below.
		receivedCh := make(chan struct{}) // channel closed when the notification has been acked.
		mctx.Debug("sending UIPaymentReview bid:%v sessionID:%v seqno:%v nextButton:%v banners:%v",
			arg.Bid, arg.SessionID, seqno, nextButton, len(banners))
		for _, banner := range banners {
			mctx.Debug("banner: %+v", banner)
		}
		go func() {
			err := stellarUI.PaymentReviewed(mctx.Ctx(), stellar1.PaymentReviewedArg{
				SessionID: arg.SessionID,
				Msg: stellar1.UIPaymentReviewed{
					Bid:        arg.Bid,
					ReviewID:   arg.ReviewID,
					Seqno:      seqno,
					Banners:    banners,
					NextButton: string(nextButton),
				},
			})
			if err != nil {
				mctx.Debug("error in response to UIPaymentReview: %v", err)
			}
			close(receivedCh)
		}()
		return receivedCh
	}

	if !data.ReadyToReview {
		// Caller goofed.
		<-notify([]stellar1.SendBannerLocal{{
			Level:   "error",
			Message: "This payment is not ready to review",
		}}, reviewButtonDisabled)
		return fmt.Errorf("this payment is not ready to review")
	}
	if data.Frozen == nil {
		// Should be impossible.
		return fmt.Errorf("this payment is missing values")
	}

	notify(nil, reviewButtonSpinning)

	wantFollowingCheck := true

	if data.Frozen.ToIsAccountID {
		mctx.Debug("skipping identify for account ID recipient: %v", data.Frozen.To)
		data.ReadyToSend = true
		wantFollowingCheck = false
	}

	recipientAssertion := data.Frozen.To
	// how would you have this before identify?  from LookupRecipient?
	// does that mean that identify is happening twice?
	recipientUV := data.Frozen.ToUV

	// check if it is a federation address
	if strings.Contains(recipientAssertion, stellarAddress.Separator) {
		name, domain, err := stellarAddress.Split(recipientAssertion)
		// if there is an error, let this fall through and get identified
		if err == nil {
			if domain != "keybase.io" {
				mctx.Debug("skipping identify for federation address recipient: %s", data.Frozen.To)
				data.ReadyToSend = true
				wantFollowingCheck = false
			} else {
				mctx.Debug("identifying keybase user %s in federation address recipient: %s", name, data.Frozen.To)
				recipientAssertion = name
			}
		}
	} else if !isKeybaseAssertion(mctx, recipientAssertion) { // assume assertion resolution happened already.
		data.ReadyToSend = true
		wantFollowingCheck = false
	}

	mctx.Debug("wantFollowingCheck: %v", wantFollowingCheck)
	var stickyBanners []stellar1.SendBannerLocal
	if wantFollowingCheck {
		if isFollowing, err := isFollowingForReview(mctx, recipientAssertion); err == nil && !isFollowing {
			stickyBanners = []stellar1.SendBannerLocal{{
				Level:   "warning",
				Message: fmt.Sprintf("You are not following %v. Are you sure this is the right person?", recipientAssertion),
			}}
			notify(stickyBanners, reviewButtonSpinning)
		}
	}

	if !data.ReadyToSend {
		mctx.Debug("identifying recipient: %v", recipientAssertion)

		identifySuccessCh := make(chan struct{}, 1)
		identifyTrackFailCh := make(chan struct{}, 1)
		identifyErrCh := make(chan error, 1)

		// Forward notifications about successful identifies of this recipient.
		go func() {
			unsubscribe, globalSuccessCh := mctx.G().IdentifyDispatch.Subscribe(mctx)
			defer unsubscribe()
			for {
				select {
				case <-mctx.Ctx().Done():
					return
				case idRes := <-globalSuccessCh:
					if recipientUV.IsNil() || !idRes.Target.Equal(recipientUV.Uid) {
						continue
					}
					mctx.Debug("review forwarding identify success")
					select {
					case <-mctx.Ctx().Done():
						return
					case identifySuccessCh <- struct{}{}:
					}
				}
			}
		}()

		// Start an identify in the background.
		go identifyForReview(mctx, recipientAssertion,
			identifySuccessCh, identifyTrackFailCh, identifyErrCh)

	waiting:
		for {
			select {
			case <-mctx.Ctx().Done():
				return mctx.Ctx().Err()
			case <-identifyErrCh:
				stickyBanners = nil
				notify([]stellar1.SendBannerLocal{{
					Level:   "error",
					Message: fmt.Sprintf("Error while identifying %v. Please check your network and try again.", recipientAssertion),
				}}, reviewButtonDisabled)
			case <-identifyTrackFailCh:
				stickyBanners = nil
				notify([]stellar1.SendBannerLocal{{
					Level:         "error",
					Message:       fmt.Sprintf("Some of %v's proofs have changed since you last followed them.", recipientAssertion),
					ProofsChanged: true,
				}}, reviewButtonDisabled)
			case <-identifySuccessCh:
				data.ReadyToSend = true
				break waiting
			}
		}
	}

	if err := mctx.Ctx().Err(); err != nil {
		return err
	}
	receivedEnableCh := notify(stickyBanners, reviewButtonEnabled)

	// Stay open until this call gets canceled or until frontend
	// acks a notification that enables the button.
	select {
	case <-receivedEnableCh:
	case <-mctx.Ctx().Done():
	}
	return mctx.Ctx().Err()
}