This commit is contained in:
Evgeniy Kulikov 2019-11-18 16:34:06 +03:00
commit 1cf33e5ffd
No known key found for this signature in database
GPG key ID: BF6AEE0A2A699BF2
87 changed files with 29835 additions and 0 deletions

8
accounting/fixtures/cheque.sh Executable file
View file

@ -0,0 +1,8 @@
#!/bin/bash
CHEQUE=d6520dabb6cb9b981792608c73670eff14775e9a65bbc189271723ba2703c53263e8d6e522dc32203339dcd8eee9c6b7439a0000000053724e000000000000001e61000603012d47e76210aec73be39ab3d186e0a40fe8d86bfa3d4fabfda57ba13b88f96abe1de4c7ecd46cb32081c0ff199e0b32708d2ce709dd146ce096484073a9b15a259ca799f8d848eb5bea16f6d0842a0181ccd47384af2cdb0fd0af0819e8a08802f7528ce97c9a93558efe7d4f62577aabdf771c931f54a71be6ad21e7d9cc1777686ad19b5dc4b80d7b8decf90054c5aad66c0e6fe63d8473b751cd77c1bd0557516e0f3e7d0ccb485809023b0c08a89f33ae38b2f99ce3f1ebc7905dddf0ed0f023e00f03a16e8707ce045eb42ee80d392451541ee510dc18e1c8befbac54d7426087d37d32d836537d317deafbbd193002a36f80fbdfbf3a730cf011bc6c75c7e6d5724f3adee7015fcb3068d321e2ae555e79107be0c46070efdae2f724dbc9f0340750b92789821683283bcb98e32b7e032b94f267b6964613fc31a7ce5813fddeea47a1db525634237e924178b5c8ea745549ae60aa3570ce6cf52e370e6ab87652bdf8a179176f1acaf48896bef9ab300818a53f410d86241d506a550f4915403fef27f744e829131d0ec980829fafa51db1714c2761d9f78762c008c323e9d6612e4f9efdc609f191fd9ca5431dd9dc037130150107ab8769780d728e9ffdf314019b57c8d2b940b9ec078afa951ed8b06c1bf352edd2037e29b8f24cca3ec700368a6f5829fb2a34fa03d0308ae6b05f433f2904d9a852fed1f5d2eb598ca79475b74ef6394e712d275cd798062c6d8e41fad822ac5a4fcb167f0a2e196f61f9f65a0adef9650f49150e7eb7bb08dd1739fa6e86b341f1b2cf5657fcd200637e8
DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
echo $CHEQUE | xxd -p -r > $DIR/cheque_data
exit 0

Binary file not shown.

49
accounting/service.go Normal file
View file

@ -0,0 +1,49 @@
package accounting
import (
"github.com/nspcc-dev/neofs-proto/decimal"
"github.com/nspcc-dev/neofs-proto/internal"
"github.com/nspcc-dev/neofs-proto/refs"
)
type (
// OwnerID type alias.
OwnerID = refs.OwnerID
// Decimal type alias.
Decimal = decimal.Decimal
// Filter is used to filter accounts by criteria.
Filter func(acc *Account) bool
)
const (
// ErrEmptyAddress is raised when passed Address is empty.
ErrEmptyAddress = internal.Error("empty address")
// ErrEmptyLockTarget is raised when passed LockTarget is empty.
ErrEmptyLockTarget = internal.Error("empty lock target")
// ErrEmptyContainerID is raised when passed CID is empty.
ErrEmptyContainerID = internal.Error("empty container ID")
// ErrEmptyParentAddress is raised when passed ParentAddress is empty.
ErrEmptyParentAddress = internal.Error("empty parent address")
)
// SetTTL sets ttl to BalanceRequest to satisfy TTLRequest interface.
func (m BalanceRequest) SetTTL(v uint32) { m.TTL = v }
// SumFunds goes through all accounts and sums up active funds.
func SumFunds(accounts []*Account) (res *decimal.Decimal) {
res = decimal.Zero.Copy()
for i := range accounts {
if accounts[i] == nil {
continue
}
res = res.Add(accounts[i].ActiveFunds)
}
return
}

701
accounting/service.pb.go Normal file
View file

@ -0,0 +1,701 @@
// Code generated by protoc-gen-gogo. DO NOT EDIT.
// source: accounting/service.proto
package accounting
import (
context "context"
fmt "fmt"
_ "github.com/gogo/protobuf/gogoproto"
proto "github.com/golang/protobuf/proto"
decimal "github.com/nspcc-dev/neofs-proto/decimal"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
io "io"
math "math"
math_bits "math/bits"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
type BalanceRequest struct {
OwnerID OwnerID `protobuf:"bytes,1,opt,name=OwnerID,proto3,customtype=OwnerID" json:"OwnerID"`
TTL uint32 `protobuf:"varint,2,opt,name=TTL,proto3" json:"TTL,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *BalanceRequest) Reset() { *m = BalanceRequest{} }
func (m *BalanceRequest) String() string { return proto.CompactTextString(m) }
func (*BalanceRequest) ProtoMessage() {}
func (*BalanceRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_7f9514b8f1d4c7fe, []int{0}
}
func (m *BalanceRequest) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *BalanceRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
func (m *BalanceRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_BalanceRequest.Merge(m, src)
}
func (m *BalanceRequest) XXX_Size() int {
return m.Size()
}
func (m *BalanceRequest) XXX_DiscardUnknown() {
xxx_messageInfo_BalanceRequest.DiscardUnknown(m)
}
var xxx_messageInfo_BalanceRequest proto.InternalMessageInfo
func (m *BalanceRequest) GetTTL() uint32 {
if m != nil {
return m.TTL
}
return 0
}
type BalanceResponse struct {
Balance *decimal.Decimal `protobuf:"bytes,1,opt,name=Balance,proto3" json:"Balance,omitempty"`
LockAccounts []*Account `protobuf:"bytes,2,rep,name=LockAccounts,proto3" json:"LockAccounts,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *BalanceResponse) Reset() { *m = BalanceResponse{} }
func (m *BalanceResponse) String() string { return proto.CompactTextString(m) }
func (*BalanceResponse) ProtoMessage() {}
func (*BalanceResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_7f9514b8f1d4c7fe, []int{1}
}
func (m *BalanceResponse) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *BalanceResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
func (m *BalanceResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_BalanceResponse.Merge(m, src)
}
func (m *BalanceResponse) XXX_Size() int {
return m.Size()
}
func (m *BalanceResponse) XXX_DiscardUnknown() {
xxx_messageInfo_BalanceResponse.DiscardUnknown(m)
}
var xxx_messageInfo_BalanceResponse proto.InternalMessageInfo
func (m *BalanceResponse) GetBalance() *decimal.Decimal {
if m != nil {
return m.Balance
}
return nil
}
func (m *BalanceResponse) GetLockAccounts() []*Account {
if m != nil {
return m.LockAccounts
}
return nil
}
func init() {
proto.RegisterType((*BalanceRequest)(nil), "accounting.BalanceRequest")
proto.RegisterType((*BalanceResponse)(nil), "accounting.BalanceResponse")
}
func init() { proto.RegisterFile("accounting/service.proto", fileDescriptor_7f9514b8f1d4c7fe) }
var fileDescriptor_7f9514b8f1d4c7fe = []byte{
// 311 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x48, 0x4c, 0x4e, 0xce,
0x2f, 0xcd, 0x2b, 0xc9, 0xcc, 0x4b, 0xd7, 0x2f, 0x4e, 0x2d, 0x2a, 0xcb, 0x4c, 0x4e, 0xd5, 0x2b,
0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x42, 0xc8, 0x48, 0x89, 0xa6, 0xa4, 0x26, 0x67, 0xe6, 0x26,
0xe6, 0xe8, 0x43, 0x69, 0x88, 0x12, 0x29, 0x31, 0x24, 0xcd, 0x25, 0x95, 0x05, 0xa9, 0xc5, 0x50,
0x71, 0xdd, 0xf4, 0xcc, 0x92, 0x8c, 0xd2, 0x24, 0xbd, 0xe4, 0xfc, 0x5c, 0xfd, 0xf4, 0xfc, 0xf4,
0x7c, 0x7d, 0xb0, 0x70, 0x52, 0x69, 0x1a, 0x98, 0x07, 0xe6, 0x80, 0x59, 0x10, 0xe5, 0x4a, 0xbe,
0x5c, 0x7c, 0x4e, 0x89, 0x39, 0x89, 0x79, 0xc9, 0xa9, 0x41, 0xa9, 0x85, 0xa5, 0xa9, 0xc5, 0x25,
0x42, 0x9a, 0x5c, 0xec, 0xfe, 0xe5, 0x79, 0xa9, 0x45, 0x9e, 0x2e, 0x12, 0x8c, 0x0a, 0x8c, 0x1a,
0x3c, 0x4e, 0xfc, 0x27, 0xee, 0xc9, 0x33, 0xdc, 0xba, 0x27, 0x0f, 0x13, 0x0e, 0x82, 0x31, 0x84,
0x04, 0xb8, 0x98, 0x43, 0x42, 0x7c, 0x24, 0x98, 0x14, 0x18, 0x35, 0x78, 0x83, 0x40, 0x4c, 0xa5,
0x32, 0x2e, 0x7e, 0xb8, 0x71, 0xc5, 0x05, 0xf9, 0x79, 0xc5, 0xa9, 0x42, 0x5a, 0x5c, 0xec, 0x50,
0x21, 0xb0, 0x79, 0xdc, 0x46, 0x02, 0x7a, 0x30, 0x9f, 0xb8, 0x40, 0xe8, 0x20, 0x98, 0x02, 0x21,
0x73, 0x2e, 0x1e, 0x9f, 0xfc, 0xe4, 0x6c, 0x47, 0x88, 0xd7, 0x8a, 0x25, 0x98, 0x14, 0x98, 0x35,
0xb8, 0x8d, 0x84, 0xf5, 0x10, 0x7e, 0xd5, 0x83, 0xca, 0x05, 0xa1, 0x28, 0x34, 0x0a, 0xe0, 0xe2,
0x72, 0x84, 0xab, 0x11, 0x72, 0x82, 0x5b, 0x29, 0x24, 0x85, 0xac, 0x17, 0xd5, 0xa7, 0x52, 0xd2,
0x58, 0xe5, 0x20, 0xce, 0x76, 0x72, 0x3c, 0xf1, 0x48, 0x8e, 0xf1, 0xc2, 0x23, 0x39, 0xc6, 0x1b,
0x8f, 0xe4, 0x18, 0x1f, 0x3c, 0x92, 0x63, 0x9c, 0xf1, 0x58, 0x8e, 0x21, 0x4a, 0x1b, 0x29, 0x74,
0xf3, 0x8a, 0x0b, 0x92, 0x93, 0x75, 0x53, 0x52, 0xcb, 0xf4, 0xf3, 0x52, 0xf3, 0xd3, 0x8a, 0x75,
0x21, 0x61, 0x8b, 0x30, 0x32, 0x89, 0x0d, 0x2c, 0x62, 0x0c, 0x08, 0x00, 0x00, 0xff, 0xff, 0x0c,
0x45, 0x3c, 0x0a, 0xe8, 0x01, 0x00, 0x00,
}
// Reference imports to suppress errors if they are not otherwise used.
var _ context.Context
var _ grpc.ClientConn
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
const _ = grpc.SupportPackageIsVersion4
// AccountingClient is the client API for Accounting service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type AccountingClient interface {
Balance(ctx context.Context, in *BalanceRequest, opts ...grpc.CallOption) (*BalanceResponse, error)
}
type accountingClient struct {
cc *grpc.ClientConn
}
func NewAccountingClient(cc *grpc.ClientConn) AccountingClient {
return &accountingClient{cc}
}
func (c *accountingClient) Balance(ctx context.Context, in *BalanceRequest, opts ...grpc.CallOption) (*BalanceResponse, error) {
out := new(BalanceResponse)
err := c.cc.Invoke(ctx, "/accounting.Accounting/Balance", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// AccountingServer is the server API for Accounting service.
type AccountingServer interface {
Balance(context.Context, *BalanceRequest) (*BalanceResponse, error)
}
// UnimplementedAccountingServer can be embedded to have forward compatible implementations.
type UnimplementedAccountingServer struct {
}
func (*UnimplementedAccountingServer) Balance(ctx context.Context, req *BalanceRequest) (*BalanceResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method Balance not implemented")
}
func RegisterAccountingServer(s *grpc.Server, srv AccountingServer) {
s.RegisterService(&_Accounting_serviceDesc, srv)
}
func _Accounting_Balance_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(BalanceRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(AccountingServer).Balance(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/accounting.Accounting/Balance",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(AccountingServer).Balance(ctx, req.(*BalanceRequest))
}
return interceptor(ctx, in, info, handler)
}
var _Accounting_serviceDesc = grpc.ServiceDesc{
ServiceName: "accounting.Accounting",
HandlerType: (*AccountingServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "Balance",
Handler: _Accounting_Balance_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "accounting/service.proto",
}
func (m *BalanceRequest) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *BalanceRequest) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *BalanceRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if m.TTL != 0 {
i = encodeVarintService(dAtA, i, uint64(m.TTL))
i--
dAtA[i] = 0x10
}
{
size := m.OwnerID.Size()
i -= size
if _, err := m.OwnerID.MarshalTo(dAtA[i:]); err != nil {
return 0, err
}
i = encodeVarintService(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0xa
return len(dAtA) - i, nil
}
func (m *BalanceResponse) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *BalanceResponse) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *BalanceResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if len(m.LockAccounts) > 0 {
for iNdEx := len(m.LockAccounts) - 1; iNdEx >= 0; iNdEx-- {
{
size, err := m.LockAccounts[iNdEx].MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintService(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x12
}
}
if m.Balance != nil {
{
size, err := m.Balance.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintService(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func encodeVarintService(dAtA []byte, offset int, v uint64) int {
offset -= sovService(v)
base := offset
for v >= 1<<7 {
dAtA[offset] = uint8(v&0x7f | 0x80)
v >>= 7
offset++
}
dAtA[offset] = uint8(v)
return base
}
func (m *BalanceRequest) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
l = m.OwnerID.Size()
n += 1 + l + sovService(uint64(l))
if m.TTL != 0 {
n += 1 + sovService(uint64(m.TTL))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *BalanceResponse) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.Balance != nil {
l = m.Balance.Size()
n += 1 + l + sovService(uint64(l))
}
if len(m.LockAccounts) > 0 {
for _, e := range m.LockAccounts {
l = e.Size()
n += 1 + l + sovService(uint64(l))
}
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func sovService(x uint64) (n int) {
return (math_bits.Len64(x|1) + 6) / 7
}
func sozService(x uint64) (n int) {
return sovService(uint64((x << 1) ^ uint64((int64(x) >> 63))))
}
func (m *BalanceRequest) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowService
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: BalanceRequest: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: BalanceRequest: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field OwnerID", wireType)
}
var byteLen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowService
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
byteLen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if byteLen < 0 {
return ErrInvalidLengthService
}
postIndex := iNdEx + byteLen
if postIndex < 0 {
return ErrInvalidLengthService
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if err := m.OwnerID.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 2:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field TTL", wireType)
}
m.TTL = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowService
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.TTL |= uint32(b&0x7F) << shift
if b < 0x80 {
break
}
}
default:
iNdEx = preIndex
skippy, err := skipService(dAtA[iNdEx:])
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthService
}
if (iNdEx + skippy) < 0 {
return ErrInvalidLengthService
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *BalanceResponse) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowService
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: BalanceResponse: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: BalanceResponse: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Balance", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowService
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthService
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthService
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.Balance == nil {
m.Balance = &decimal.Decimal{}
}
if err := m.Balance.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field LockAccounts", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowService
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthService
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthService
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.LockAccounts = append(m.LockAccounts, &Account{})
if err := m.LockAccounts[len(m.LockAccounts)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipService(dAtA[iNdEx:])
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthService
}
if (iNdEx + skippy) < 0 {
return ErrInvalidLengthService
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func skipService(dAtA []byte) (n int, err error) {
l := len(dAtA)
iNdEx := 0
depth := 0
for iNdEx < l {
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowService
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
wireType := int(wire & 0x7)
switch wireType {
case 0:
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowService
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
iNdEx++
if dAtA[iNdEx-1] < 0x80 {
break
}
}
case 1:
iNdEx += 8
case 2:
var length int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowService
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
length |= (int(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
if length < 0 {
return 0, ErrInvalidLengthService
}
iNdEx += length
case 3:
depth++
case 4:
if depth == 0 {
return 0, ErrUnexpectedEndOfGroupService
}
depth--
case 5:
iNdEx += 4
default:
return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
}
if iNdEx < 0 {
return 0, ErrInvalidLengthService
}
if depth == 0 {
return iNdEx, nil
}
}
return 0, io.ErrUnexpectedEOF
}
var (
ErrInvalidLengthService = fmt.Errorf("proto: negative length found during unmarshaling")
ErrIntOverflowService = fmt.Errorf("proto: integer overflow")
ErrUnexpectedEndOfGroupService = fmt.Errorf("proto: unexpected end of group")
)

23
accounting/service.proto Normal file
View file

@ -0,0 +1,23 @@
syntax = "proto3";
package accounting;
option go_package = "github.com/nspcc-dev/neofs-proto/accounting";
import "decimal/decimal.proto";
import "accounting/types.proto";
import "github.com/gogo/protobuf/gogoproto/gogo.proto";
option (gogoproto.stable_marshaler_all) = true;
service Accounting {
rpc Balance(BalanceRequest) returns (BalanceResponse);
}
message BalanceRequest {
bytes OwnerID = 1 [(gogoproto.customtype) = "OwnerID", (gogoproto.nullable) = false];
uint32 TTL = 2;
}
message BalanceResponse {
decimal.Decimal Balance = 1;
repeated Account LockAccounts = 2;
}

353
accounting/types.go Normal file
View file

@ -0,0 +1,353 @@
package accounting
import (
"crypto/ecdsa"
"crypto/rand"
"encoding/binary"
"reflect"
"github.com/mr-tron/base58"
crypto "github.com/nspcc-dev/neofs-crypto"
"github.com/nspcc-dev/neofs-proto/chain"
"github.com/nspcc-dev/neofs-proto/decimal"
"github.com/nspcc-dev/neofs-proto/internal"
"github.com/nspcc-dev/neofs-proto/refs"
"github.com/pkg/errors"
)
type (
// Cheque structure that describes a user request for withdrawal of funds.
Cheque struct {
ID ChequeID
Owner refs.OwnerID
Amount *decimal.Decimal
Height uint64
Signatures []ChequeSignature
}
// BalanceReceiver interface that is used to retrieve user balance by address.
BalanceReceiver interface {
Balance(accountAddress string) (*Account, error)
}
// ChequeID is identifier of user request for withdrawal of funds.
ChequeID string
// CID type alias.
CID = refs.CID
// SGID type alias.
SGID = refs.SGID
// ChequeSignature contains public key and hash, and is used to verify signatures.
ChequeSignature struct {
Key *ecdsa.PublicKey
Hash []byte
}
)
const (
// ErrWrongSignature is raised when wrong signature is passed.
ErrWrongSignature = internal.Error("wrong signature")
// ErrWrongPublicKey is raised when wrong public key is passed.
ErrWrongPublicKey = internal.Error("wrong public key")
// ErrWrongChequeData is raised when passed bytes cannot not be parsed as valid Cheque.
ErrWrongChequeData = internal.Error("wrong cheque data")
// ErrInvalidLength is raised when passed bytes cannot not be parsed as valid ChequeID.
ErrInvalidLength = internal.Error("invalid length")
u16size = 2
u64size = 8
signaturesOffset = chain.AddressLength + refs.OwnerIDSize + u64size + u64size
)
// NewChequeID generates valid random ChequeID using crypto/rand.Reader.
func NewChequeID() (ChequeID, error) {
d := make([]byte, chain.AddressLength)
if _, err := rand.Read(d); err != nil {
return "", err
}
id := base58.Encode(d)
return ChequeID(id), nil
}
// String returns string representation of ChequeID.
func (b ChequeID) String() string { return string(b) }
// Empty returns true, if ChequeID is empty.
func (b ChequeID) Empty() bool { return len(b) == 0 }
// Valid validates ChequeID.
func (b ChequeID) Valid() bool {
d, err := base58.Decode(string(b))
return err == nil && len(d) == chain.AddressLength
}
// Bytes returns bytes representation of ChequeID.
func (b ChequeID) Bytes() []byte {
d, err := base58.Decode(string(b))
if err != nil {
return make([]byte, chain.AddressLength)
}
return d
}
// Equal checks that current ChequeID is equal to passed ChequeID.
func (b ChequeID) Equal(b2 ChequeID) bool {
return b.Valid() && b2.Valid() && string(b) == string(b2)
}
// Unmarshal tries to parse []byte into valid ChequeID.
func (b *ChequeID) Unmarshal(data []byte) error {
*b = ChequeID(base58.Encode(data))
if !b.Valid() {
return ErrInvalidLength
}
return nil
}
// Size returns size (chain.AddressLength).
func (b ChequeID) Size() int {
return chain.AddressLength
}
// MarshalTo tries to marshal ChequeID into passed bytes and returns
// count of copied bytes or error, if bytes len is not enough to contain ChequeID.
func (b ChequeID) MarshalTo(data []byte) (int, error) {
if len(data) < chain.AddressLength {
return 0, ErrInvalidLength
}
return copy(data, b.Bytes()), nil
}
// Equals checks that m and tx are valid and equal Tx values.
func (m Tx) Equals(tx Tx) bool {
return m.From == tx.From &&
m.To == tx.To &&
m.Type == tx.Type &&
m.Amount == tx.Amount
}
// Verify validates current Cheque and Signatures that are generated for current Cheque.
func (b Cheque) Verify() error {
data := b.marshalBody()
for i, sign := range b.Signatures {
if err := crypto.VerifyRFC6979(sign.Key, data, sign.Hash); err != nil {
return errors.Wrapf(ErrWrongSignature, "item #%d: %s", i, err.Error())
}
}
return nil
}
// Sign is used to sign current Cheque and stores result inside b.Signatures.
func (b *Cheque) Sign(key *ecdsa.PrivateKey) error {
hash, err := crypto.SignRFC6979(key, b.marshalBody())
if err != nil {
return err
}
b.Signatures = append(b.Signatures, ChequeSignature{
Key: &key.PublicKey,
Hash: hash,
})
return nil
}
func (b *Cheque) marshalBody() []byte {
buf := make([]byte, signaturesOffset)
var offset int
offset += copy(buf, b.ID.Bytes())
offset += copy(buf[offset:], b.Owner.Bytes())
binary.BigEndian.PutUint64(buf[offset:], uint64(b.Amount.Value))
offset += u64size
binary.BigEndian.PutUint64(buf[offset:], b.Height)
return buf
}
func (b *Cheque) unmarshalBody(buf []byte) error {
var offset int
if len(buf) < signaturesOffset {
return ErrWrongChequeData
}
{ // unmarshal UUID
if err := b.ID.Unmarshal(buf[offset : offset+chain.AddressLength]); err != nil {
return err
}
offset += chain.AddressLength
}
{ // unmarshal OwnerID
if err := b.Owner.Unmarshal(buf[offset : offset+refs.OwnerIDSize]); err != nil {
return err
}
offset += refs.OwnerIDSize
}
{ // unmarshal amount
amount := int64(binary.BigEndian.Uint64(buf[offset:]))
b.Amount = decimal.New(amount)
offset += u64size
}
{ // unmarshal height
b.Height = binary.BigEndian.Uint64(buf[offset:])
offset += u64size
}
return nil
}
// MarshalBinary is used to marshal Cheque into bytes.
func (b Cheque) MarshalBinary() ([]byte, error) {
var (
count = len(b.Signatures)
buf = make([]byte, b.Size())
offset = copy(buf, b.marshalBody())
)
binary.BigEndian.PutUint16(buf[offset:], uint16(count))
offset += u16size
for _, sign := range b.Signatures {
key := crypto.MarshalPublicKey(sign.Key)
offset += copy(buf[offset:], key)
offset += copy(buf[offset:], sign.Hash)
}
return buf, nil
}
// Size returns size of Cheque (count of bytes needs to store it).
func (b Cheque) Size() int {
return signaturesOffset + u16size +
len(b.Signatures)*(crypto.PublicKeyCompressedSize+crypto.RFC6979SignatureSize)
}
// UnmarshalBinary tries to parse []byte into valid Cheque.
func (b *Cheque) UnmarshalBinary(buf []byte) error {
if err := b.unmarshalBody(buf); err != nil {
return err
}
body := buf[:signaturesOffset]
count := int64(binary.BigEndian.Uint16(buf[signaturesOffset:]))
offset := signaturesOffset + u16size
if ln := count * int64(crypto.PublicKeyCompressedSize+crypto.RFC6979SignatureSize); ln > int64(len(buf[offset:])) {
return ErrWrongChequeData
}
for i := int64(0); i < count; i++ {
sign := ChequeSignature{
Key: crypto.UnmarshalPublicKey(buf[offset : offset+crypto.PublicKeyCompressedSize]),
Hash: make([]byte, crypto.RFC6979SignatureSize),
}
offset += crypto.PublicKeyCompressedSize
if sign.Key == nil {
return errors.Wrapf(ErrWrongPublicKey, "item #%d", i)
}
offset += copy(sign.Hash, buf[offset:offset+crypto.RFC6979SignatureSize])
if err := crypto.VerifyRFC6979(sign.Key, body, sign.Hash); err != nil {
return errors.Wrapf(ErrWrongSignature, "item #%d: %s (offset=%d, len=%d)", i, err.Error(), offset, len(sign.Hash))
}
b.Signatures = append(b.Signatures, sign)
}
return nil
}
// ErrNotEnoughFunds generates error using address and amounts.
func ErrNotEnoughFunds(addr string, needed, residue *decimal.Decimal) error {
return errors.Errorf("not enough funds (requested=%s, residue=%s, addr=%s", needed, residue, addr)
}
func (m *Account) hasLockAcc(addr string) bool {
for i := range m.LockAccounts {
if m.LockAccounts[i].Address == addr {
return true
}
}
return false
}
// ValidateLock checks that account can be locked.
func (m *Account) ValidateLock() error {
switch {
case m.Address == "":
return ErrEmptyAddress
case m.ParentAddress == "":
return ErrEmptyParentAddress
case m.LockTarget == nil:
return ErrEmptyLockTarget
}
switch v := m.LockTarget.Target.(type) {
case *LockTarget_WithdrawTarget:
if v.WithdrawTarget.Cheque != m.Address {
return errors.Errorf("wrong cheque ID: expected %s, has %s", m.Address, v.WithdrawTarget.Cheque)
}
case *LockTarget_ContainerCreateTarget:
switch {
case v.ContainerCreateTarget.CID.Empty():
return ErrEmptyContainerID
}
}
return nil
}
// CanLock checks possibility to lock funds.
func (m *Account) CanLock(lockAcc *Account) error {
switch {
case m.ActiveFunds.LT(lockAcc.ActiveFunds):
return ErrNotEnoughFunds(lockAcc.ParentAddress, lockAcc.ActiveFunds, m.ActiveFunds)
case m.hasLockAcc(lockAcc.Address):
return errors.Errorf("could not lock account(%s) funds: duplicating lock(%s)", m.Address, lockAcc.Address)
default:
return nil
}
}
// LockForWithdraw checks that account contains locked funds by passed ChequeID.
func (m *Account) LockForWithdraw(chequeID string) bool {
switch v := m.LockTarget.Target.(type) {
case *LockTarget_WithdrawTarget:
return v.WithdrawTarget.Cheque == chequeID
}
return false
}
// LockForContainerCreate checks that account contains locked funds for container creation.
func (m *Account) LockForContainerCreate(cid refs.CID) bool {
switch v := m.LockTarget.Target.(type) {
case *LockTarget_ContainerCreateTarget:
return v.ContainerCreateTarget.CID.Equal(cid)
}
return false
}
// Equal checks that current Settlement is equal to passed Settlement.
func (m *Settlement) Equal(s *Settlement) bool {
if s == nil || m.Epoch != s.Epoch || len(m.Transactions) != len(s.Transactions) {
return false
}
return len(m.Transactions) == 0 || reflect.DeepEqual(m.Transactions, s.Transactions)
}

3905
accounting/types.pb.go Normal file

File diff suppressed because it is too large Load diff

106
accounting/types.proto Normal file
View file

@ -0,0 +1,106 @@
syntax = "proto3";
package accounting;
option go_package = "github.com/nspcc-dev/neofs-proto/accounting";
import "decimal/decimal.proto";
import "github.com/gogo/protobuf/gogoproto/gogo.proto";
option (gogoproto.stable_marshaler_all) = true;
// Snapshot accounting messages
message Account {
bytes OwnerID = 1 [(gogoproto.customtype) = "OwnerID", (gogoproto.nullable) = false];
string Address = 2;
string ParentAddress = 3;
decimal.Decimal ActiveFunds = 4;
Lifetime Lifetime = 5 [(gogoproto.nullable) = false];
LockTarget LockTarget = 6;
repeated Account LockAccounts = 7;
}
message LockTarget {
oneof Target {
WithdrawTarget WithdrawTarget = 1;
ContainerCreateTarget ContainerCreateTarget = 2;
}
}
// Snapshot balance messages
message Balances {
repeated Account Accounts = 1 [(gogoproto.nullable) = false];
}
// PayIn / PayOut messages
message PayIO {
uint64 BlockID = 1;
repeated Tx Transactions = 2 [(gogoproto.nullable) = false];
}
// Clearing messages
message Clearing {
repeated Tx Transactions = 1 [(gogoproto.nullable) = false];
}
// Clearing messages
message Withdraw {
string ID = 1;
uint64 Epoch = 2;
Tx Transaction = 3;
}
// Lifetime of locks
message Lifetime {
enum Unit {
Unlimited = 0;
NeoFSEpoch = 1;
NeoBlock = 2;
}
Unit unit = 1 [(gogoproto.customname) = "Unit"];
int64 Value = 2;
}
// Transaction messages
message Tx {
enum Type {
Unknown = 0;
Withdraw = 1;
PayIO = 2;
Inner = 3;
}
Type type = 1 [(gogoproto.customname) = "Type"];
string From = 2;
string To = 3;
decimal.Decimal Amount = 4;
bytes PublicKeys = 5; // of sender
}
message Settlement {
message Receiver {
string To = 1;
decimal.Decimal Amount = 2;
}
message Container {
bytes CID = 1 [(gogoproto.customtype) = "CID", (gogoproto.nullable) = false];
repeated bytes SGIDs = 2 [(gogoproto.customtype) = "SGID", (gogoproto.nullable) = false];
}
message Tx {
string From = 1;
Container Container = 2 [(gogoproto.nullable) = false];
repeated Receiver Receivers = 3 [(gogoproto.nullable) = false];
}
uint64 Epoch = 1;
repeated Tx Transactions = 2;
}
message ContainerCreateTarget {
bytes CID = 1 [(gogoproto.customtype) = "CID", (gogoproto.nullable) = false];
}
message WithdrawTarget {
string Cheque = 1;
}

84
accounting/types_test.go Normal file
View file

@ -0,0 +1,84 @@
package accounting
import (
"io/ioutil"
"testing"
"github.com/mr-tron/base58"
"github.com/nspcc-dev/neofs-crypto/test"
"github.com/nspcc-dev/neofs-proto/chain"
"github.com/nspcc-dev/neofs-proto/decimal"
"github.com/nspcc-dev/neofs-proto/refs"
"github.com/stretchr/testify/require"
)
func TestCheque(t *testing.T) {
t.Run("new/valid", func(t *testing.T) {
id, err := NewChequeID()
require.NoError(t, err)
require.True(t, id.Valid())
d := make([]byte, chain.AddressLength+1)
// expected size + 1 byte
str := base58.Encode(d)
require.False(t, ChequeID(str).Valid())
// expected size - 1 byte
str = base58.Encode(d[:len(d)-2])
require.False(t, ChequeID(str).Valid())
// wrong encoding
d = d[:len(d)-1] // normal size
require.False(t, ChequeID(string(d)).Valid())
})
t.Run("marshal/unmarshal", func(t *testing.T) {
var b2 = new(Cheque)
key1 := test.DecodeKey(0)
key2 := test.DecodeKey(1)
id, err := NewChequeID()
require.NoError(t, err)
owner, err := refs.NewOwnerID(&key1.PublicKey)
require.NoError(t, err)
b1 := &Cheque{
ID: id,
Owner: owner,
Height: 100,
Amount: decimal.NewGAS(100),
}
require.NoError(t, b1.Sign(key1))
require.NoError(t, b1.Sign(key2))
data, err := b1.MarshalBinary()
require.NoError(t, err)
require.Len(t, data, b1.Size())
require.NoError(t, b2.UnmarshalBinary(data))
require.Equal(t, b1, b2)
require.NoError(t, b1.Verify())
require.NoError(t, b2.Verify())
})
t.Run("example from SC", func(t *testing.T) {
var pathToCheque = "fixtures/cheque_data"
expect, err := ioutil.ReadFile(pathToCheque)
require.NoError(t, err)
var cheque Cheque
require.NoError(t, cheque.UnmarshalBinary(expect))
actual, err := cheque.MarshalBinary()
require.NoError(t, err)
require.Equal(t, expect, actual)
require.NoError(t, cheque.Verify())
})
}

53
accounting/withdraw.go Normal file
View file

@ -0,0 +1,53 @@
package accounting
import (
"encoding/binary"
"github.com/nspcc-dev/neofs-proto/refs"
)
type (
// MessageID type alias.
MessageID = refs.MessageID
)
// SetTTL sets ttl to GetRequest to satisfy TTLRequest interface.
func (m *GetRequest) SetTTL(v uint32) { m.TTL = v }
// SetTTL sets ttl to PutRequest to satisfy TTLRequest interface.
func (m *PutRequest) SetTTL(v uint32) { m.TTL = v }
// SetTTL sets ttl to ListRequest to satisfy TTLRequest interface.
func (m *ListRequest) SetTTL(v uint32) { m.TTL = v }
// SetTTL sets ttl to DeleteRequest to satisfy TTLRequest interface.
func (m *DeleteRequest) SetTTL(v uint32) { m.TTL = v }
// SetSignature sets signature to PutRequest to satisfy SignedRequest interface.
func (m *PutRequest) SetSignature(v []byte) { m.Signature = v }
// SetSignature sets signature to DeleteRequest to satisfy SignedRequest interface.
func (m *DeleteRequest) SetSignature(v []byte) { m.Signature = v }
// PrepareData prepares bytes representation of PutRequest to satisfy SignedRequest interface.
func (m *PutRequest) PrepareData() ([]byte, error) {
var offset int
// MessageID-len + OwnerID-len + Amount + Height
buf := make([]byte, refs.UUIDSize+refs.OwnerIDSize+binary.MaxVarintLen64+binary.MaxVarintLen64)
offset += copy(buf[offset:], m.MessageID.Bytes())
offset += copy(buf[offset:], m.OwnerID.Bytes())
offset += binary.PutVarint(buf[offset:], m.Amount.Value)
binary.PutUvarint(buf[offset:], m.Height)
return buf, nil
}
// PrepareData prepares bytes representation of DeleteRequest to satisfy SignedRequest interface.
func (m *DeleteRequest) PrepareData() ([]byte, error) {
var offset int
// ID-len + OwnerID-len + MessageID-len
buf := make([]byte, refs.UUIDSize+refs.OwnerIDSize+refs.UUIDSize)
offset += copy(buf[offset:], m.ID.Bytes())
offset += copy(buf[offset:], m.OwnerID.Bytes())
copy(buf[offset:], m.MessageID.Bytes())
return buf, nil
}

2641
accounting/withdraw.pb.go Normal file

File diff suppressed because it is too large Load diff

61
accounting/withdraw.proto Normal file
View file

@ -0,0 +1,61 @@
syntax = "proto3";
package accounting;
option go_package = "github.com/nspcc-dev/neofs-proto/accounting";
import "decimal/decimal.proto";
import "github.com/gogo/protobuf/gogoproto/gogo.proto";
option (gogoproto.stable_marshaler_all) = true;
service Withdraw {
rpc Get(GetRequest) returns (GetResponse);
rpc Put(PutRequest) returns (PutResponse);
rpc List(ListRequest) returns (ListResponse);
rpc Delete(DeleteRequest) returns (DeleteResponse);
}
message Item {
bytes ID = 1 [(gogoproto.customtype) = "ChequeID", (gogoproto.nullable) = false];
bytes OwnerID = 2 [(gogoproto.customtype) = "OwnerID", (gogoproto.nullable) = false];
decimal.Decimal Amount = 3;
uint64 Height = 4;
bytes Payload = 5;
}
message GetRequest {
bytes ID = 1 [(gogoproto.customtype) = "ChequeID", (gogoproto.nullable) = false];
bytes OwnerID = 2 [(gogoproto.customtype) = "OwnerID", (gogoproto.nullable) = false];
uint32 TTL = 3;
}
message GetResponse {
Item Withdraw = 1;
}
message PutRequest {
bytes OwnerID = 1 [(gogoproto.customtype) = "OwnerID", (gogoproto.nullable) = false];
decimal.Decimal Amount = 2;
uint64 Height = 3;
bytes MessageID = 4 [(gogoproto.customtype) = "MessageID", (gogoproto.nullable) = false];
bytes Signature = 5;
uint32 TTL = 6;
}
message PutResponse {
bytes ID = 1 [(gogoproto.customtype) = "ChequeID", (gogoproto.nullable) = false];
}
message ListRequest {
bytes OwnerID = 1 [(gogoproto.customtype) = "OwnerID", (gogoproto.nullable) = false];
uint32 TTL = 2;
}
message ListResponse {
repeated Item Items = 1;
}
message DeleteRequest {
bytes ID = 1 [(gogoproto.customtype) = "ChequeID", (gogoproto.nullable) = false];
bytes OwnerID = 2 [(gogoproto.customtype) = "OwnerID", (gogoproto.nullable) = false];
bytes MessageID = 3 [(gogoproto.customtype) = "MessageID", (gogoproto.nullable) = false];
bytes Signature = 4;
uint32 TTL = 5;
}
message DeleteResponse {}