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: storage.go
    path of file: ./repos/chainlink/core/services/s4
    
    the code of the file until where you have to start completion: package s4

import (
	"context"

	"github.com/jonboulle/clockwork"

	"github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big"
	"github.com/smartcontractkit/chainlink/v2/core/logger"
	"github.com/smartcontractkit/chainlink/v2/core/services/pg"

	"github.com/ethereum/go-ethereum/common"
)

// Constraints specifies the global storage constraints.
type Constraints struct {
	MaxPayloadSizeBytes    uint   `json:"maxPayloadSizeBytes"`
	MaxSlotsPerUser        uint   `json:"maxSlotsPerUser"`
	MaxExpirationLengthSec uint64 `json:"maxExpirationLengthSec"`
}

// Key identifies a versioned user record.
type Key struct {
	// Address is a user address
	Address common.Address
	// SlotId is a slot number
	SlotId uint
	// Version is a data version
	Version uint64
}

// Reco
    
func (s *storage) Put(ctx context.Context, key *Key, record *Record, signature []byte) error {
	if key.SlotId >= s.contraints.MaxSlotsPerUser {
		return ErrSlotIdTooBig
	}
	if len(record.Payload) > int(s.contraints.MaxPayloadSizeBytes) {
		return ErrPayloadTooBig
	}
	now := s.clock.Now().UnixMilli()
	if now > record.Expiration {
		return ErrPastExpiration
	}
	if record.Expiration-now > int64(s.contraints.MaxExpirationLengthSec)*1000 {
		return ErrExpirationTooLong
	}

	envelope := NewEnvelopeFromRecord(key, record)
	signer, err := envelope.GetSignerAddress(signature)
	if err != nil || signer != key.Address {
		return ErrWrongSignature
	}

	row := &Row{
		Address:    big.New(key.Address.Big()),
		SlotId:     key.SlotId,
		Payload:    make([]byte, len(record.Payload)),
		Version:    key.Version,
		Expiration: record.Expiration,
		Confirmed:  false,
		Signature:  make([]byte, len(signature)),
	}
	copy(row.Payload, record.Payload)
	copy(row.Signature, signature)

	return s.orm.Update(row, pg.WithParentCtx(ctx))
}