Repository: NethermindEth/juno
Pull request #140 (2022-04-19 23:49:33)

Add BlockNumber type
## Changes:

Implement the BlockNumber type to handle the `block_number`field in the feeder models. A `block_number` can be an integer equal to or greater than 0 or the string "pending"

## Types of changes

What types of changes does your code introduce?
_Put an `x` in the boxes that apply_

- [ ] Bugfix (non-breaking change which fixes an issue)
- [x] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] Documentation Update
- [ ] Code style update (formatting, renaming)
- [ ] Refactoring (no functional changes, no api changes)
- [ ] Build related changes
- [ ] Other (please describe): 

## Testing
**Requires testing**

- [x] Yes
- [ ] No

**In case you checked yes, did you write tests??**

- [x] Yes
- [ ] No

codecov[bot]: # [Codecov](https://codecov.io/gh/NethermindEth/juno/pull/140?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=NethermindEth) Report
> Merging [#140](https://codecov.io/gh/NethermindEth/juno/pull/140?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=NethermindEth) (03daed1) into [main](https://codecov.io/gh/NethermindEth/juno/commit/285c917849c05f314db18110fc27ec10a8d81e33?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=NethermindEth) (285c917) will **not change** coverage.
> The diff coverage is `100.00%`.

```diff
@@            Coverage Diff            @@
##              main      #140   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           17        18    +1     
  Lines         1098      1118   +20     
=========================================
+ Hits          1098      1118   +20     
```

| Flag | Coverage Δ | |
|---|---|---|
| unittests | `100.00% <100.00%> (ø)` | |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=NethermindEth#carryforward-flags-in-the-pull-request-comment) to find out more.

| [Impacted Files](https://codecov.io/gh/NethermindEth/juno/pull/140?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=NethermindEth) | Coverage Δ | |
|---|---|---|
| [pkg/feeder/types/blockNumber.go](https://codecov.io/gh/NethermindEth/juno/pull/140/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=NethermindEth#diff-cGtnL2ZlZWRlci90eXBlcy9ibG9ja051bWJlci5nbw==) | `100.00% <100.00%> (ø)` | |
| [pkg/rpc/server.go](https://codecov.io/gh/NethermindEth/juno/pull/140/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=NethermindEth#diff-cGtnL3JwYy9zZXJ2ZXIuZ28=) | `100.00% <0.00%> (ø)` | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/NethermindEth/juno/pull/140?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=NethermindEth).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=NethermindEth)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/NethermindEth/juno/pull/140?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=NethermindEth). Last update [285c917...03daed1](https://codecov.io/gh/NethermindEth/juno/pull/140?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=NethermindEth). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=NethermindEth).

stdevAdrianPaez: > Could you please use this type in the places is used the int implementation of BlockNumber?
> 

Yes, ok
stdevAdrianPaez: @stdevMac The `StarkNetBlock` has other issues with JSON decoding, these are the issues when I try to decode a block:

- [ ] `StarknetBlock.Transactions` must be an array of `TxnSpecificInfo`
- [ ] `TxnSpecificInfo.Type` must be of type `string`
- [ ] `StarkNetBlock.TransactionReceipts` must be an array of `TransactionExecution`
- [ ] `Event.Keys` must be of type `[]string`
- [ ] `Event.Data` must be of type `[]strign`
- [ ] `ExecutionResources.NMemoryHoles` must be of type `int64`

Patch:
diff --git a/pkg/crypto/internal/randutil/randutil.go b/pkg/crypto/internal/randutil/randutil.go
index feed8760..713c6ebe 100644
--- a/pkg/crypto/internal/randutil/randutil.go
+++ b/pkg/crypto/internal/randutil/randutil.go
@@ -32,6 +32,7 @@ func MaybeReadByte(r io.Reader) {
 	select {
 	case <-closedChan:
 		return
+	// notest
 	case <-closedChan:
 		var buf [1]byte
 		r.Read(buf[:])
diff --git a/pkg/feeder/types.go b/pkg/feeder/types.go
index 59704e9b..338a42ef 100644
--- a/pkg/feeder/types.go
+++ b/pkg/feeder/types.go
@@ -1,7 +1,10 @@
 package feeder
 
 // notest
-import "github.com/NethermindEth/juno/pkg/rpc"
+import (
+	"github.com/NethermindEth/juno/pkg/feeder/types"
+	"github.com/NethermindEth/juno/pkg/rpc"
+)
 
 type (
 	// TxnType represents the type of each transaction.
@@ -145,7 +148,7 @@ type TransactionExecution struct {
 type StarknetBlock struct {
 	BlockHash           string               `json:"block_hash"`
 	ParentBlockHash     string               `json:"parent_block_hash"`
-	BlockNumber         string               `json:"block_number"`
+	BlockNumber         types.BlockNumber    `json:"block_number"`
 	StateRoot           string               `json:"state_root"`
 	Status              rpc.BlockStatus      `json:"status"`
 	Transactions        TxnSpecificInfo      `json:"transactions"`
diff --git a/pkg/feeder/types/blockNumber.go b/pkg/feeder/types/blockNumber.go
new file mode 100644
index 00000000..40306a1b
--- /dev/null
+++ b/pkg/feeder/types/blockNumber.go
@@ -0,0 +1,49 @@
+package types
+
+import (
+	"encoding/json"
+	"fmt"
+	"strings"
+)
+
+const pending = "pending"
+
+// BlockNumber represents the `block_number` field in the StarkNet block
+// structure. The BlockNumber is an int64, but in StarkNet the value can be an
+// integer or the string "pending", so to check if the BlockNumber is pending
+// the BlockNumber type implements the boolean function IsPending()
+//
+// See https://docs.starknet.io/docs/Blocks/header#block-header
+type BlockNumber int64
+
+func (x *BlockNumber) IsPending() bool {
+	return int64(*x) == -1
+}
+
+func (x *BlockNumber) UnmarshalJSON(data []byte) error {
+	if len(data) == 0 {
+		return fmt.Errorf("empty data")
+	}
+	// Try to unmarshal as "pending"
+	if data[0] == '"' {
+		var s string
+		if err := json.Unmarshal(data, &s); err != nil {
+			return err
+		}
+		if strings.Compare(pending, s) != 0 {
+			return fmt.Errorf("unexpected string value %s as a BlockNumber", s)
+		}
+		*x = BlockNumber(-1)
+	} else {
+		// Try to unmarshal as number
+		var value int64
+		if err := json.Unmarshal(data, &value); err != nil {
+			return err
+		}
+		if value < 0 {
+			return fmt.Errorf("the block number must be greater than zero")
+		}
+		*x = BlockNumber(value)
+	}
+	return nil
+}


Test patch:
diff --git a/pkg/feeder/types/blockNumber_test.go b/pkg/feeder/types/blockNumber_test.go
new file mode 100644
index 00000000..da2af0c0
--- /dev/null
+++ b/pkg/feeder/types/blockNumber_test.go
@@ -0,0 +1,86 @@
+package types
+
+import "testing"
+
+func TestIsPending(t *testing.T) {
+	var tests = [...]struct {
+		blockNumber BlockNumber
+		want        bool
+	}{
+		{
+			blockNumber: BlockNumber(-1),
+			want:        true,
+		},
+		{
+			blockNumber: BlockNumber(0),
+			want:        false,
+		},
+		{
+			blockNumber: BlockNumber(10),
+			want:        false,
+		},
+	}
+	for _, test := range tests {
+		result := test.blockNumber.IsPending()
+		if result != test.want {
+			t.Errorf("%d.IsPending()=%t, want %t", test.blockNumber, result, test.want)
+		}
+	}
+}
+
+func TestBlockNumberUnmarshalJSON(t *testing.T) {
+	var tests = [...]struct {
+		data []byte
+		want BlockNumber
+		err  bool
+	}{
+		{
+			data: []byte("\"pending\""),
+			want: BlockNumber(-1),
+			err:  false,
+		},
+		{
+			data: []byte("-1"),
+			want: *new(BlockNumber),
+			err:  true,
+		},
+		{
+			data: []byte("123"),
+			want: BlockNumber(123),
+			err:  false,
+		},
+		{
+			data: []byte("123\""),
+			want: *new(BlockNumber),
+			err:  true,
+		},
+		{
+			data: []byte("\"123"),
+			want: *new(BlockNumber),
+			err:  true,
+		},
+		{
+			data: []byte(""),
+			want: *new(BlockNumber),
+			err:  true,
+		},
+		{
+			data: []byte("\"invalid string\""),
+			want: *new(BlockNumber),
+			err:  true,
+		},
+	}
+	for _, test := range tests {
+		result := *new(BlockNumber)
+		err := result.UnmarshalJSON(test.data)
+		if err != nil && !test.err {
+			t.Errorf("unexpected error: %s", err.Error())
+		}
+		if err == nil && test.err {
+			t.Errorf("test must fail with error")
+		}
+		if result != test.want {
+			t.Errorf("UnmarshalJSON(%v)=%d, want %d", test.data, result, test.want)
+		}
+	}
+}