func TransferProposal(sk *[ed25519.PrivateKeySize]byte, name string,
	profile *Profile) *SignedProfileOperation {
	return OldSign(sk, MakeOperation(name, profile))
}
Creates a signed operation structure to transfer name to profile. To make
this change take effect, the recipient has to call AcceptTransfer with the
secret key whose public counterpart is in profile.
package client
import (
	"bytes"
	"golang.org/x/net/proxy"
	"fmt"
	"github.com/agl/ed25519"
	"github.com/andres-erbsen/chatterbox/transport"
	. "github.com/andres-erbsen/dename/protocol"
	"github.com/gogo/protobuf/proto"
	"io"
	"net"
	"time"
)
var true_ = true
var pad_to uint64 = 4 << 10
type verifier struct {
	name string
	pk   *Profile_PublicKey
}
type server struct {
	address     string
	timeout     time.Duration
	transportPK [32]byte
}
type Client struct {
	now                         func() time.Time
	dialer                      proxy.Dialer
	freshnessThreshold          time.Duration
	freshnessSignaturesRequired int
	consensusSignaturesRequired int
	verifier                    map[uint64]*verifier
	update                      []*server
	lookup                      []*server
}
func (c *Client) connect(s *server) (*transport.Conn, error) {
	var plainconn net.Conn
	plainconn, err := c.dialer.Dial("tcp", s.address)
	if err != nil {
		return nil, fmt.Errorf("dial %s: %s", s.address, err)
	}
	plainconn.SetDeadline(time.Now().Add(s.timeout))
	conn, _, err := transport.Handshake(plainconn, nil, nil, &s.transportPK, 1<<12)
	if err != nil {
		plainconn.Close()
		return nil, fmt.Errorf("transport handshake %s: %s", s.address, err)
	}
	return conn, nil
}
func (c *Client) atSomeServer(servers []*server, f func(*transport.Conn) (bool, error)) (err error) {
	for _, server := range servers {
		var conn *transport.Conn
		conn, err = c.connect(server)
		if err != nil {
			continue
		}
		var done bool
		done, err = f(conn)
		conn.Close()
		if done {
			return err
		}
	}
	return err
}
func (c *Client) Lookup(name string) (profile *Profile, err error) {
	profile, _, err = c.LookupReply(name)
	return
}
func (c *Client) LookupReply(name string) (profile *Profile, reply *ClientReply, err error) {
	err = c.atSomeServer(c.lookup, func(conn *transport.Conn) (bool, error) {
		rq := &ClientMessage{PeekState: &true_, ResolveName: []byte(name), PadReplyTo: &pad_to}
		if _, err = conn.WriteFrame(Pad(PBEncode(rq), 256)); err != nil {
			return false, err
		}
		if reply, err = readReply(conn); err != nil {
			return false, err
		}
		if profile, err = c.LookupFromReply(name, reply); err != nil {
			return true, err
		}
		return true, nil
	})
	return
}
func (c *Client) LookupFromReply(name string, reply *ClientReply) (profile *Profile, err error) {
	var root []byte
	root, err = c.VerifyConsensus(reply.StateConfirmations)
	if err != nil {
		return
	}
	profileBs, err := VerifyResolveAgainstRoot(root, name, reply.LookupNodes)
	if err != nil {
		return
	}
	if profileBs == nil {
		profile = nil
		return
	}
	profile = new(Profile)
	err = proto.Unmarshal(profileBs, profile)
	if err != nil {
		return nil, err
	}
	expirationTime := time.Unix(int64(*profile.ExpirationTime), 0)
	if expirationTime.Before(time.Now().Add(MAX_VALIDITY_PERIOD * time.Second / 2)) {
		return profile, errOutOfDate{fmt.Sprintf("the profile is out of date and will be erased completely on %s", expirationTime)}
	}
	return profile, nil
}
var (
	ErrRegistrationDisabled = fmt.Errorf("registration disabled")
	ErrInviteInvalid        = fmt.Errorf("invite not valid")
	ErrInviteUsed           = fmt.Errorf("invite already used")
	ErrNotAuthorized        = fmt.Errorf("not authorized")
	ErrCouldntVerify        = fmt.Errorf("could not verify the correctness of the response")
)
type errOutOfDate struct {
	string
}
func (e errOutOfDate) Error() string {
	return e.string
}
func IsErrOutOfDate(err error) bool {
	switch err.(type) {
	case errOutOfDate:
		return true
	default:
		return false
	}
}
func (c *Client) Enact(op *SignedProfileOperation, invite []byte) (err error) {
	err = c.atSomeServer(c.update, func(conn *transport.Conn) (bool, error) {
		msg := &ClientMessage{ModifyProfile: op, InviteCode: invite}
		_, err = conn.WriteFrame(Pad(PBEncode(msg), int(pad_to)))
		if err != nil {
			return false, err
		}
		var reply *ClientReply
		reply, err = readReply(conn)
		if err != nil {
			return false, err
		}
		switch reply.GetStatus() {
		case ClientReply_OK:
			return true, nil
		case ClientReply_REGISTRATION_DISABLED:
			return false, ErrRegistrationDisabled
		case ClientReply_INVITE_INVALID:
			return false, ErrInviteInvalid
		case ClientReply_INVITE_USED:
			return false, ErrInviteUsed
		case ClientReply_NOT_AUTHORIZED:
			return false, ErrNotAuthorized
		default:
			return false, fmt.Errorf("unknown status code")
		}
	})
	return
}
type byteReader struct{ io.Reader }
func (r byteReader) ReadByte() (byte, error) {
	var ret [1]byte
	_, err := io.ReadFull(r, ret[:])
	return ret[0], err
}
func readReply(conn *transport.Conn) (reply *ClientReply, err error) {
	buf := make([]byte, pad_to)
	sz, err := conn.ReadFrame(buf)
	if err != nil {
		return
	}
	reply = new(ClientReply)
	err = proto.Unmarshal(Unpad(buf[:sz]), reply)
	return
}
func MakeOperation(name string, profile *Profile) *SignedProfileOperation {
	return &SignedProfileOperation{
		ProfileOperation: PBEncode(&SignedProfileOperation_ProfileOperationT{
			Name:       []byte(name),
			NewProfile: PBEncode(profile),
		}),
	}
}