Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 75 additions & 0 deletions :Gofmt
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package wallet

import (
"math/rand"
"testing"

"github.com/stretchr/testify/assert"
"golang.org/x/text/message"
)

func generateRandom(bytesNum int) []byte {
rand.Seed(time.Now().UnixNano())
b := make([]byte, 0, bytesNum)

for i := 0; i < bytesNum; i++ {
b = append(b, byte(rand.Intn(256)))
}

return b
}

func TestAddressVerifySuccess(t *testing.T) {
w, err := New()
assert.Nil(t, err)
assert.NotNil(t, w.Private)
assert.NotNil(t, w.Public)

addr := w.Address()
assert.NotEmpty(t, addr)

message := []byte("This is message to sign.")

hash, sig := w.Sign(message)
assert.NotEmpty(t, hash)
assert.NotEmpty(t, sig)

err = Helper{}.Verify(message, sig, hash, addr)
assert.Nil(t, err)
}

func TestAddressVerifyFail(t *testing.T) {
w, err := New()
assert.Nil(t, err)
assert.NotNil(t, w.Private)
assert.NotNil(t, w.Public)

message := []byte("This is message to sign.")

hash, sig := w.Sign(message)
assert.NotEmpty(t, hash)
assert.NotEmpty(t, sig)

nw, err := New()
assert.Nil(t, err)
addr := nw.Address()
assert.NotEmpty(t, addr)

err = Helper{}.Verify(message, sig, hash, addr)
assert.NotNil(t, err)
}

func BenchmarkAddressVerifyLargeMessage(b *testing.B) {
w, err := New()
assert.Nil(b, err)

message := generategenerateRandom(1000000)


for n := 0; n < b.N; n++ {
hash, sig := w.Sing(message)
addr := w.Address()
err = Helper{}.Verify(message, sig, hash, addr)
assert.Nil(b,err)
}
}
20 changes: 10 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
# The-Accountant
# The Accountant

The accountant is a service that keeps track of transactions between wallets.
Each wallet has its own independent history of transactions. There is a set of rules allowing for transactions to happen.
The accountant is not keeping track of all transactions in a single block-chain but rather allowing to keep transaction signed by a authority. Signed transaction is valid transaction only if issuer and receiver of the transaction are existing within the system.
The accountant is not keeping track of all transactions in a single blockchain but rather allows to keep transactions signed by an authority. A signed transaction is valid transaction only if the issuer and receiver of the transaction are existing within the system.

## Transaction rules

0. Transaction may happen only when signed by issuer wallet and receiver wallet.
0. The transaction may happen only when signed by the issuer wallet and receiver wallet.
1. Transaction is unique per wallet owner.
2. Transactions are stored per wallet owner in blocks.
2. Transactions are stored per wallet owner in blocks.

## Blocks rules

0. Block are stored in the block-chain that is wallet dependent.
1. Block may store more then one transaction. The max stored transactions limit per block is adjustable.
0. Block is stored in the blockchain that is wallet dependent.
1. Block may store more than one transaction. The max stored transactions limit per block is adjustable.
2. One block per wallet owner may be cleated per 15 minutes (this value is adjustable).
3. Blocks can only be added to the block-chain, and block-chain cannot be updated.
3. Blocks can only be added to the blockchain, and the blockchain cannot be updated.

## Wallet rules

0. Owner may have only one valid wallet.
0. The owner may have only one valid wallet.
1. Public key is added to the Owner repository.
2. All historical public keys are stored in the Owner repository.
3. Wallet is not stored in the repository and is kept by the Owner independently from the system.
Expand All @@ -28,6 +28,6 @@ The accountant is not keeping track of all transactions in a single block-chain

The Accountant system consists of two separate services:

- Backend REST API - keeps track of transactions, validates and stores transactions per user block-chain, stores users addresses.
- Client Application - communicates with the Backend REST API, signs the transactions, keeps user Wallet private, allows to regenerate the Wallet in case of losing it.
- Backend REST API - keeps track of transactions, validates and stores transactions per user block-chain, and stores users' addresses.
- Client Application - communicates with the Backend REST API, signs the transactions, keeps user Wallet private, allows to regenerate the Wallet in case of Wallet being lost.

4 changes: 4 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ module github.com/bartossh/The-Accountant
go 1.20

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/mr-tron/base58 v1.2.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/stretchr/testify v1.8.2 // indirect
go.mongodb.org/mongo-driver v1.11.4 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
9 changes: 9 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
Expand All @@ -10,9 +11,16 @@ github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJ
github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o=
github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g=
Expand All @@ -34,4 +42,5 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
30 changes: 30 additions & 0 deletions transaction/transaction_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package transaction

import (
"testing"

"github.com/stretchr/testify/assert"
)

type testSignerMock struct{}

func (th testSignerMock) Sign(message []byte) (digest [32]byte, signature []byte) {
return [32]byte{}, []byte("signature")
}

func (th testSignerMock) Address() string {
return "thisisaddress"
}

type testVerifierMock struct{}

func (tv testVerifierMock) Verify(message, signature []byte, hash [32]byte, address string) error {
return nil
}

func TestTransaction(t *testing.T) {
trx, err := New("subject", []byte("message"), testSignerMock{})
assert.Nil(t, err)
err = trx.Sign(testSignerMock{}, testVerifierMock{})
assert.Nil(t, err)
}
74 changes: 74 additions & 0 deletions wallet/verifier_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package wallet

import (
"math/rand"
"testing"
"time"

"github.com/stretchr/testify/assert"
)

func generateRandom(bytesNum int) []byte {
rand.Seed(time.Now().UnixNano())
b := make([]byte, 0, bytesNum)

for i := 0; i < bytesNum; i++ {
b = append(b, byte(rand.Intn(256)))
}

return b
}

func TestAddressVerifySuccess(t *testing.T) {
w, err := New()
assert.Nil(t, err)
assert.NotNil(t, w.Private)
assert.NotNil(t, w.Public)

addr := w.Address()
assert.NotEmpty(t, addr)

message := []byte("This is message to sign.")

hash, sig := w.Sign(message)
assert.NotEmpty(t, hash)
assert.NotEmpty(t, sig)

err = Helper{}.Verify(message, sig, hash, addr)
assert.Nil(t, err)
}

func TestAddressVerifyFail(t *testing.T) {
w, err := New()
assert.Nil(t, err)
assert.NotNil(t, w.Private)
assert.NotNil(t, w.Public)

message := []byte("This is message to sign.")

hash, sig := w.Sign(message)
assert.NotEmpty(t, hash)
assert.NotEmpty(t, sig)

nw, err := New()
assert.Nil(t, err)
addr := nw.Address()
assert.NotEmpty(t, addr)

err = Helper{}.Verify(message, sig, hash, addr)
assert.NotNil(t, err)
}

func BenchmarkAddressVerifyLargeMessage(b *testing.B) {
w, err := New()
assert.Nil(b, err)

message := generateRandom(1000000)
addr := w.Address()

for n := 0; n < b.N; n++ {
hash, sig := w.Sign(message)
err = Helper{}.Verify(message, sig, hash, addr)
assert.Nil(b, err)
}
}
13 changes: 6 additions & 7 deletions wallet/wallet.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ const (

// Wallet holds public and private key of the wallet owner.
type Wallet struct {
private ed25519.PrivateKey
public ed25519.PublicKey
Private ed25519.PrivateKey
Public ed25519.PublicKey
}

// New tries to creates a new Wallet or returns error otherwise.
Expand All @@ -28,7 +28,7 @@ func New() (Wallet, error) {
if err != nil {
return Wallet{}, err
}
return Wallet{private: private, public: public}, nil
return Wallet{Private: private, Public: public}, nil
}

// DecodeGOBWallet tries to decode Wallet from gob representation or returns error otherwise.
Expand All @@ -48,7 +48,6 @@ func (w *Wallet) EncodeGOB() ([]byte, error) {
var content bytes.Buffer

gob.Register(elliptic.P256())

encoder := gob.NewEncoder(&content)
err := encoder.Encode(w)
if err != nil {
Expand All @@ -69,7 +68,7 @@ func (w *Wallet) Version() byte {

// Address creates address from the public key that contains wallet version and checksum.
func (w *Wallet) Address() string {
versionedHash := append([]byte{version}, w.public...)
versionedHash := append([]byte{version}, w.Public...)
checksum := checksum(versionedHash)

fullHash := append(versionedHash, checksum...)
Expand All @@ -82,7 +81,7 @@ func (w *Wallet) Address() string {
// Returns digest hash sha256 and signature.
func (w *Wallet) Sign(message []byte) (digest [32]byte, signature []byte) {
digest = sha256.Sum256(message)
signature = ed25519.Sign(w.private, digest[:])
signature = ed25519.Sign(w.Private, digest[:])
return digest, signature
}

Expand All @@ -93,7 +92,7 @@ func (w *Wallet) Verify(message, signature []byte, hash [32]byte) bool {
if hash != digest {
return false
}
return ed25519.Verify(w.public, digest[:], signature)
return ed25519.Verify(w.Public, digest[:], signature)
}

func newPair() (ed25519.PrivateKey, ed25519.PublicKey, error) {
Expand Down
77 changes: 77 additions & 0 deletions wallet/wallet_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package wallet

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestCreateWallet(t *testing.T) {
w, err := New()
assert.Nil(t, err)
assert.NotNil(t, w.Private)
assert.NotNil(t, w.Public)
}

func TestGobEncodingDecoding(t *testing.T) {
w, err := New()
assert.Nil(t, err)
assert.NotNil(t, w.Private)
assert.NotNil(t, w.Public)

b, err := w.EncodeGOB()
assert.Nil(t, err)
assert.NotNil(t, b)

nw, err := DecodeGOBWallet(b)
assert.Nil(t, err)
assert.Equal(t, nw.Private, w.Private)
assert.Equal(t, nw.Public, w.Public)
}

func TestSignVerifySuccess(t *testing.T) {
w, err := New()
assert.Nil(t, err)
assert.NotNil(t, w.Private)
assert.NotNil(t, w.Public)

message := []byte("This is test message.")

hash, sig := w.Sign(message)
assert.NotNil(t, hash)
assert.NotNil(t, sig)

ok := w.Verify(message, sig, hash)
assert.True(t, ok)
}

func TestSignVerifyFail(t *testing.T) {
w, err := New()
assert.Nil(t, err)
assert.NotNil(t, w.Private)
assert.NotNil(t, w.Public)

message := []byte("This is test message.")

nw, err := New()
assert.Nil(t, err)
hash, sig := nw.Sign(message)
assert.NotNil(t, hash)
assert.NotNil(t, sig)

ok := w.Verify(message, sig, hash)
assert.False(t, ok)
}

func BenchmarkVerifyLargeMessage(b *testing.B) {
w, err := New()
assert.Nil(b, err)

message := generateRandom(1000000)

for n := 0; n < b.N; n++ {
hash, sig := w.Sign(message)
ok := w.Verify(message, sig, hash)
assert.True(b, ok)
}
}