Skip to content
Open
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
17 changes: 6 additions & 11 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ go-build:
go-test:
go test -v github.com/onflow/rosetta/state/...
go test -v github.com/onflow/rosetta/script/...
# Compile (but don't run) the build-tagged localnet test so it can't rot.
go test -tags localnet -run '^$$' github.com/onflow/rosetta/localnettest/...

# End-to-end localnet compatibility test (script/README.md). Requires a flow-go
# localnet up at 127.0.0.1:4001, the flow CLI, jq, and python3 with click +
Expand All @@ -39,16 +41,9 @@ gen-originator-account:
echo "Private Key: $$PRIVATE_KEY"; \
address=$$(flow accounts create --sig-algo ECDSA_secp256k1 --key $$PUBLIC_FLOW_KEY $(FLOW_CLI_FLAGS) | grep "Address" | cut -d' ' -f2 | cut -c3-);\
echo "Address created: $$address"; \
jq --arg account_name "$(ACCOUNT_NAME)" '.accounts[$$account_name] = { \
"address": "'$$address'", \
"key": { \
"type": "hex", \
"index": 0, \
"signatureAlgorithm": "ECDSA_secp256k1", \
"hashAlgorithm": "SHA3_256", \
"privateKey": "'$$PRIVATE_KEY'" \
} \
}' "${FLOW_JSON}" > flow.json.tmp && mv flow.json.tmp "${FLOW_JSON}" || { echo "Failed to update ${FLOW_JSON} with jq"; exit 1; }; \
jq --arg account_name "$(ACCOUNT_NAME)" --arg address "$$address" --arg private_key "$$PRIVATE_KEY" \
'.accounts[$$account_name] = {address: $$address, key: {type: "hex", index: 0, signatureAlgorithm: "ECDSA_secp256k1", hashAlgorithm: "SHA3_256", privateKey: $$private_key}}' \
"${FLOW_JSON}" > flow.json.tmp && mv flow.json.tmp "${FLOW_JSON}" || { echo "Failed to update ${FLOW_JSON} with jq"; exit 1; }; \
jq --arg address "$$address" '.originators += [$$address]' "${ROSETTA_ENV}.json" > env.json.tmp && mv env.json.tmp "${ROSETTA_ENV}.json"; \
echo "$(ACCOUNT_NAME),$$KEYS,0x$$address" >> $(ACCOUNT_KEYS_FILENAME); \
echo "Updated $(FLOW_JSON), $(ROSETTA_ENV).json and $(ACCOUNT_KEYS_FILENAME)";
Expand Down Expand Up @@ -88,7 +83,7 @@ rosetta-transfer-funds:
echo "Payer address: $$PAYER_ADDRESS"; \
RECIPIENT_ADDRESS=$$(grep '$(RECIPIENT_NAME)' $(ACCOUNT_KEYS_FILENAME) | cut -d ',' -f5); \
echo "Recipient address: $$RECIPIENT_ADDRESS"; \
TX_HASH=$$(python3 rosetta_handler.py rosetta-transfer-funds $(ROSETTA_HOST_URL) $$PAYER_ADDRESS $$PAYER_PUBLIC_KEY $$PAYER_PRIVATE_KEY $$RECIPIENT_ADDRESS $$AMOUNT); \
TX_HASH=$$(python3 rosetta_handler.py rosetta-transfer-funds $(ROSETTA_HOST_URL) $$PAYER_ADDRESS $$PAYER_PUBLIC_KEY $$PAYER_PRIVATE_KEY $$RECIPIENT_ADDRESS $$AMOUNT) && \
echo "Funding sent: $$TX_HASH";

# Use this target to verify that the accounts configured in the Rosetta environment JSON have the specified contracts deployed
Expand Down
75 changes: 40 additions & 35 deletions indexdb/indexdb.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,12 @@ var (
ErrBlockNotIndexed = errors.New("indexdb: block not indexed")
)

var (
accountPrefix = []byte("a")
blockPrefix = []byte("b")
hash2HeightPrefix = []byte("c")
height2HashPrefix = []byte("d")
isProxyPrefix = []byte("p")
const (
accountPrefix byte = 'a'
blockPrefix byte = 'b'
hash2HeightPrefix byte = 'c'
height2HashPrefix byte = 'd'
isProxyPrefix byte = 'p'
)

// NOTE(tav): We store the blockchain data within Badger using the following
Expand Down Expand Up @@ -88,9 +88,10 @@ func (s *Store) Accounts() (map[[8]byte]bool, error) {
err := s.db.View(func(txn *badger.Txn) error {
it := txn.NewIterator(badger.IteratorOptions{})
defer it.Close()
it.Seek(accountPrefix)
prefix := []byte{accountPrefix}
it.Seek(prefix)
for {
if !it.ValidForPrefix(accountPrefix) {
if !it.ValidForPrefix(prefix) {
break
}
key := it.Item().Key()
Expand All @@ -101,9 +102,10 @@ func (s *Store) Accounts() (map[[8]byte]bool, error) {
}
it = txn.NewIterator(badger.IteratorOptions{})
defer it.Close()
it.Seek(isProxyPrefix)
prefix = []byte{isProxyPrefix}
it.Seek(prefix)
for {
if !it.ValidForPrefix(isProxyPrefix) {
if !it.ValidForPrefix(prefix) {
break
}
key := it.Item().Key()
Expand Down Expand Up @@ -133,9 +135,10 @@ func (s *Store) AccountsInfo() (map[string]*AccountInfo, error) {
err := s.db.View(func(txn *badger.Txn) error {
it := txn.NewIterator(badger.IteratorOptions{})
defer it.Close()
it.Seek(accountPrefix)
prefix := []byte{accountPrefix}
it.Seek(prefix)
for {
if !it.ValidForPrefix(accountPrefix) {
if !it.ValidForPrefix(prefix) {
break
}
item := it.Item()
Expand Down Expand Up @@ -168,9 +171,10 @@ func (s *Store) AccountsInfo() (map[string]*AccountInfo, error) {
err = s.db.View(func(txn *badger.Txn) error {
it := txn.NewIterator(badger.IteratorOptions{})
defer it.Close()
it.Seek(isProxyPrefix)
prefix := []byte{isProxyPrefix}
it.Seek(prefix)
for {
if !it.ValidForPrefix(isProxyPrefix) {
if !it.ValidForPrefix(prefix) {
break
}
key := it.Item().Key()
Expand Down Expand Up @@ -312,7 +316,7 @@ func (s *Store) Genesis() *model.BlockMeta {
// given height.
func (s *Store) HasBalance(acct []byte, height uint64) (bool, error) {
key := make([]byte, 1+8+8)
key[0] = 'a' // accountPrefix
key[0] = accountPrefix
copy(key[1:9], acct)
binary.BigEndian.PutUint64(key[9:], height)
ok := false
Expand Down Expand Up @@ -341,7 +345,7 @@ func (s *Store) HashForHeight(height uint64) ([]byte, error) {
var hash []byte
heightEnc := make([]byte, 8)
binary.BigEndian.PutUint64(heightEnc, height)
key := append(height2HashPrefix, heightEnc...)
key := append([]byte{height2HashPrefix}, heightEnc...)
err := s.db.View(func(txn *badger.Txn) error {
item, err := txn.Get(key)
if err != nil {
Expand All @@ -367,7 +371,7 @@ func (s *Store) HashForHeight(height uint64) ([]byte, error) {
// HeightForHash returns the block height for the given hash.
func (s *Store) HeightForHash(hash []byte) (uint64, error) {
height := uint64(0)
key := append(hash2HeightPrefix, hash...)
key := append([]byte{hash2HeightPrefix}, hash...)
err := s.db.View(func(txn *badger.Txn) error {
item, err := txn.Get(key)
if err != nil {
Expand Down Expand Up @@ -427,7 +431,7 @@ func (s *Store) Index(ctx context.Context, height uint64, hash []byte, block *mo
}
if len(op.ProxyPublicKey) > 0 {
key := make([]byte, 17)
key[0] = 'p' // isProxyPrefix
key[0] = isProxyPrefix
copy(key[1:], op.Account)
binary.BigEndian.PutUint64(key[9:], height)
proxyAccts = append(proxyAccts, key)
Expand All @@ -444,7 +448,7 @@ func (s *Store) Index(ctx context.Context, height uint64, hash []byte, block *mo
updates := make([]accountUpdate, len(accts))
for acct, diff := range accts {
key := make([]byte, 1+8+8)
key[0] = 'a' // accountPrefix
key[0] = accountPrefix
copy(key[1:], acct)
copy(key[9:], hval)
updates[i] = accountUpdate{
Expand All @@ -453,13 +457,13 @@ func (s *Store) Index(ctx context.Context, height uint64, hash []byte, block *mo
}
i++
}
blockKey := append(blockPrefix, hval...)
blockKey := append([]byte{blockPrefix}, hval...)
blockValue, err := proto.Marshal(block)
if err != nil {
log.Fatalf("Failed to encode model.IndexedBlock: %s", err)
}
hash2heightKey := append(hash2HeightPrefix, hash...)
height2hashKey := append(height2HashPrefix, hval...)
hash2heightKey := append([]byte{hash2HeightPrefix}, hash...)
height2hashKey := append([]byte{height2HashPrefix}, hval...)
latest = &model.BlockMeta{
Hash: hash,
Height: height,
Expand Down Expand Up @@ -580,9 +584,10 @@ func (s *Store) PurgeProxyAccounts() {
err := s.db.View(func(txn *badger.Txn) error {
it := txn.NewIterator(badger.IteratorOptions{})
defer it.Close()
it.Seek(isProxyPrefix)
prefix := []byte{isProxyPrefix}
it.Seek(prefix)
for {
if !it.ValidForPrefix(isProxyPrefix) {
if !it.ValidForPrefix(prefix) {
break
}
key := it.Item().KeyCopy(nil)
Expand All @@ -598,7 +603,7 @@ func (s *Store) PurgeProxyAccounts() {
err = s.db.View(func(txn *badger.Txn) error {
it := txn.NewIterator(badger.IteratorOptions{})
defer it.Close()
prefix := accountPrefix
prefix := []byte{accountPrefix}
it.Seek(prefix)
for {
if !it.ValidForPrefix(prefix) {
Expand Down Expand Up @@ -643,7 +648,7 @@ func (s *Store) ResetTo(base uint64) error {
delKeys := [][]byte{}
err := s.db.View(func(txn *badger.Txn) error {
it := txn.NewIterator(badger.IteratorOptions{})
prefix := accountPrefix
prefix := []byte{accountPrefix}
it.Seek(prefix)
for {
if !it.ValidForPrefix(prefix) {
Expand All @@ -665,7 +670,7 @@ func (s *Store) ResetTo(base uint64) error {
}
err = s.db.View(func(txn *badger.Txn) error {
it := txn.NewIterator(badger.IteratorOptions{})
prefix := isProxyPrefix
prefix := []byte{isProxyPrefix}
it.Seek(prefix)
for {
if !it.ValidForPrefix(prefix) {
Expand All @@ -688,7 +693,7 @@ func (s *Store) ResetTo(base uint64) error {
last := uint64(0)
err = s.db.View(func(txn *badger.Txn) error {
it := txn.NewIterator(badger.IteratorOptions{})
prefix := height2HashPrefix
prefix := []byte{height2HashPrefix}
it.Seek(prefix)
for {
if !it.ValidForPrefix(prefix) {
Expand All @@ -701,7 +706,7 @@ func (s *Store) ResetTo(base uint64) error {
height2HashKey := item.KeyCopy(nil)
delKeys = append(delKeys, height2HashKey)
blockKey := make([]byte, 9)
blockKey[0] = 'b' // blockPrefix
blockKey[0] = blockPrefix
binary.BigEndian.PutUint64(blockKey[1:], height)
delKeys = append(delKeys, blockKey)
hash, err := item.ValueCopy(nil)
Expand All @@ -710,7 +715,7 @@ func (s *Store) ResetTo(base uint64) error {
return err
}
hash2HeightKey := make([]byte, len(hash)+1)
hash2HeightKey[0] = 'c' // hash2HeightPrefix
hash2HeightKey[0] = hash2HeightPrefix
copy(hash2HeightKey[1:], hash)
delKeys = append(delKeys, hash2HeightKey)
} else {
Expand Down Expand Up @@ -778,15 +783,15 @@ func (s *Store) SetGenesis(val *model.BlockMeta) error {
}
hval := make([]byte, 8)
binary.BigEndian.PutUint64(hval, val.Height)
blockKey := append(blockPrefix, hval...)
blockKey := append([]byte{blockPrefix}, hval...)
blockValue, err := proto.Marshal(&model.IndexedBlock{
Timestamp: val.Timestamp,
})
if err != nil {
log.Fatalf("Failed to encode model.IndexedBlock: %s", err)
}
hash2heightKey := append(hash2HeightPrefix, val.Hash...)
height2hashKey := append(height2HashPrefix, hval...)
hash2heightKey := append([]byte{hash2HeightPrefix}, val.Hash...)
height2hashKey := append([]byte{height2HashPrefix}, hval...)
err = s.db.Update(func(txn *badger.Txn) error {
if err := txn.Set([]byte("genesis"), genesis); err != nil {
return err
Expand Down Expand Up @@ -815,7 +820,7 @@ func (s *Store) SetGenesis(val *model.BlockMeta) error {
func (s *Store) balanceByHeight(acct []byte, height uint64) (uint64, error) {
balance := uint64(0)
key := make([]byte, 1+8+8)
key[0] = 'a'
key[0] = accountPrefix
copy(key[1:9], acct)
binary.BigEndian.PutUint64(key[9:], height)
err := s.db.View(func(txn *badger.Txn) error {
Expand Down Expand Up @@ -844,7 +849,7 @@ func (s *Store) balanceByHeight(acct []byte, height uint64) (uint64, error) {
func (s *Store) blockByHeight(height uint64) (*model.IndexedBlock, error) {
block := &model.IndexedBlock{}
key := make([]byte, 9)
key[0] = 'b'
key[0] = blockPrefix
binary.BigEndian.PutUint64(key[1:], height)
err := s.db.View(func(txn *badger.Txn) error {
item, err := txn.Get(key)
Expand Down
6 changes: 6 additions & 0 deletions localnettest/localnet_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ const (
originatorName = "root-originator-1"
derivedName = "derived-account-1"
transferAmount = "50"
// transferAmountUnits is transferAmount in Rosetta's smallest FLOW unit
// (8 decimals).
transferAmountUnits = 50 * 100_000_000
)

// indexerFatalErrors are server log lines meaning the indexer is wedged and will
Expand Down Expand Up @@ -103,6 +106,9 @@ func TestLocalnetCompat(t *testing.T) {

t.Log("waiting for the transfer to be indexed (recipient balance increases)")
after := waitForBalance(t, srv, base, cfg.Network, recipient, func(v uint64) bool { return v > before }, 3*time.Minute)
if after-before != transferAmountUnits {
t.Fatalf("recipient balance rose by %d, want exactly %d", after-before, uint64(transferAmountUnits))
}
t.Logf("recipient balance %d -> %d: Rosetta indexed the transfer — compatibility confirmed", before, after)
}

Expand Down
7 changes: 5 additions & 2 deletions rosetta_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,9 +151,12 @@ def rosetta_create_derived_account(rosetta_host_url, root_originator_address, ro
def rosetta_transfer_funds(rosetta_host_url, payer_address, payer_public_key,
payer_private_key, recipient_address, amount, i=0):
transaction = "transfer"
# FLOW amounts use 8 decimals (UFix64), matching the "decimals": 8 currency
# declared in the operations below.
amount = float(amount)
amount_sent = str(-1 * int(amount * 10 ** 7))
amount_received = str(int(amount * 10 ** 7))
smallest_unit = int(round(amount * 10 ** 8))
amount_sent = str(-smallest_unit)
amount_received = str(smallest_unit)
operations = [
{
"type": transaction,
Expand Down
1 change: 0 additions & 1 deletion state/convert_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,6 @@ func TestDeriveEventsHash(t *testing.T) {
spork := Mainnet28_SporkVersion8.create(ctx)
VerifyEventsHashForSpork(t, ctx, spork, 150_000_001, 150_000_011)
})

}

func VerifyEventsHashForSpork(t *testing.T, ctx context.Context, spork *config.Spork, startHeight uint64, endHeight uint64) {
Expand Down
Loading