Skip to content

Commit

Permalink
Added genesis block transactions (ava-labs#36)
Browse files Browse the repository at this point in the history
  • Loading branch information
bilgehansahin-cb authored and GitHub Enterprise committed Jul 26, 2022
1 parent 6e3e9c5 commit 7dcec0d
Show file tree
Hide file tree
Showing 6 changed files with 776 additions and 11 deletions.
47 changes: 40 additions & 7 deletions mapper/pchain/transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/ava-labs/avalanchego/utils/formatting/address"
"github.com/ava-labs/avalanchego/vms/components/avax"
"github.com/ava-labs/avalanchego/vms/platformvm"
"github.com/ava-labs/avalanchego/vms/platformvm/stakeable"
"github.com/ava-labs/avalanchego/vms/platformvm/validator"
"github.com/ava-labs/avalanchego/vms/secp256k1fx"
"github.com/coinbase/rosetta-sdk-go/parser"
Expand Down Expand Up @@ -106,6 +107,8 @@ func ParseTx(tx platformvm.UnsignedTx, isConstruction bool) (*types.Transaction,
id = v.ID()
case *platformvm.UnsignedCreateChainTx:
id = v.ID()
ops = createChainToOperation(v)

case *platformvm.UnsignedAddSubnetValidatorTx:
id = v.ID()
default:
Expand Down Expand Up @@ -137,20 +140,34 @@ func outToOperation(txOut []*avax.TransferableOutput, startIndex int, opType str

outs := make([]*types.Operation, 0)
for _, out := range txOut {
outAddrID := out.Out.(*secp256k1fx.TransferOutput).Addrs[0]
//TODO: [NM] use variables form somewhere
outAddrFormat, err := address.Format("P", "fuji", outAddrID[:])
if err != nil {
return nil, err
}

metadata := &OperationMetadata{
Type: metaType,
}

var outAddrFormat string
var transferOut avax.TransferableOut
if transferOutput, ok := out.Out.(*secp256k1fx.TransferOutput); ok {
metadata.Threshold = transferOutput.OutputOwners.Threshold
metadata.Locktime = transferOutput.OutputOwners.Locktime
transferOut = out.Out
tfOut, ok := out.Out.(*secp256k1fx.TransferOutput)
if ok {
transferOut = tfOut
}
} else if lockOut, ok := out.Out.(*stakeable.LockOut); ok {
metadata.Locktime = lockOut.Locktime
transferOut = lockOut.TransferableOut
}
transferOutput, ok := transferOut.(*secp256k1fx.TransferOutput)
if ok && transferOutput.Addrs != nil {
outAddrID := transferOutput.Addrs[0]

var err error
//TODO: [NM] use variables form somewhere
outAddrFormat, err = address.Format("P", "fuji", outAddrID[:])
if err != nil {
return nil, err
}
}

opMetadata, err := mapper.MarshalJSONMap(metadata)
Expand Down Expand Up @@ -236,6 +253,22 @@ func baseTxToOperations(tx *platformvm.BaseTx, txType string, isConstruction boo
return ins, outs, nil
}

func createChainToOperation(v *platformvm.UnsignedCreateChainTx) []*types.Operation {
return []*types.Operation{
{
OperationIdentifier: &types.OperationIdentifier{Index: 0},
Type: OpCreateChain,
Status: types.String(mapper.StatusSuccess),
Metadata: map[string]interface{}{
MetadataSubnetID: v.SubnetID.String(),
MetadataChainName: v.ChainName,
MetadataVMID: v.VMID,
MetadataMemo: v.Memo,
},
},
}
}

func rewardValidatorToOperation(v *platformvm.UnsignedRewardValidatorTx) []*types.Operation {
return []*types.Operation{
{
Expand Down
6 changes: 6 additions & 0 deletions mapper/pchain/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const (
OpAddValidator = "ADD_VALIDATOR"
OpAddDelegator = "ADD_DELEGATOR"
OpRewardValidator = "REWARD_VALIDATOR"
OpCreateChain = "CREATE_CHAIN"

OpTypeImport = "IMPORT"
OpTypeExport = "EXPORT"
Expand All @@ -15,6 +16,11 @@ const (

MetadataOpType = "type"
MetadataStakingTxId = "staking_tx"
MetadataSubnetID = "subnet_id"
MetadataChainName = "chain_name"
MetadataVMID = "vmid"
MetadataMemo = "memo"
MetadataMessage = "message"
)

var (
Expand Down
10 changes: 9 additions & 1 deletion service/backend/pchain/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,13 +98,21 @@ func (b *Backend) buildGenesisBlockResponse(ctx context.Context) (*types.BlockRe
return nil, err
}

transactions, err := b.indexerParser.GenesisToTransactions(genesisBlock)
if err != nil {
return nil, err
}

genesisBlockIdentifier := b.buildGenesisBlockIdentifier(genesisBlock)
return &types.BlockResponse{
Block: &types.Block{
BlockIdentifier: genesisBlockIdentifier,
ParentBlockIdentifier: genesisBlockIdentifier,
Transactions: []*types.Transaction{},
Transactions: transactions,
Timestamp: mapper.UnixToUnixMilli(genesisBlock.Timestamp),
Metadata: map[string]interface{}{
pmapper.MetadataMessage: genesisBlock.Message,
},
},
}, err
}
Expand Down
91 changes: 89 additions & 2 deletions service/backend/pchain/indexer/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package indexer
import (
"context"
"encoding/base64"
"errors"
"fmt"
"time"

Expand All @@ -23,12 +24,18 @@ import (
"github.com/ava-labs/avalanchego/vms/platformvm/stakeable"
"github.com/ava-labs/avalanchego/vms/proposervm/block"
"github.com/ava-labs/avalanchego/vms/secp256k1fx"
"github.com/coinbase/rosetta-sdk-go/types"

"github.com/ava-labs/avalanche-rosetta/client"
"github.com/ava-labs/avalanche-rosetta/mapper"
pmapper "github.com/ava-labs/avalanche-rosetta/mapper/pchain"
"github.com/ava-labs/avalanche-rosetta/service/backend/common"
)

var (
errUnexpectedGenesisTx = errors.New("unexpected genesis tx")
)

type Parser struct {
networkID uint32
avaxAssetID ids.ID
Expand Down Expand Up @@ -155,11 +162,18 @@ func (p *Parser) Initialize(ctx context.Context) (*ParsedGenesisBlock, error) {
utxo.UTXO.Out.InitCtx(p.ctx)
}

// Genesis commit block's parent ID is the hash of genesis state
var genesisParentID ids.ID = hashing.ComputeHash256Array(bytes)

// Genesis Block is not indexed by the indexer, but its block ID can be accessed from block 0's parent id
genesisChildBlock, _ := p.ParseBlockAtIndex(ctx, 1)
genesisBlockID := genesisChildBlock.ParentID

return &ParsedGenesisBlock{
ParsedBlock: ParsedBlock{
ParentID: ids.Empty,
ParentID: genesisParentID,
Height: 0,
BlockID: ids.Empty,
BlockID: genesisBlockID,
BlockType: "GenesisBlock",
Timestamp: p.readTime(),
Txs: txs,
Expand Down Expand Up @@ -562,3 +576,76 @@ func (p *Parser) parseTx(ctx context.Context, blkID ids.ID, tx platformvm.Tx, ge

return nil, errs.Err
}

func (p *Parser) GenesisToTransactions(genesisBlock *ParsedGenesisBlock) ([]*types.Transaction, error) {
var unsignedTx platformvm.UnsignedTx
var transactionHash string

transactions := make([]*types.Transaction, 0)
for i := range genesisBlock.Txs {
switch avTx := genesisBlock.Txs[i].(type) {
case *ParsedAddValidatorTx:
unsignedTx = &platformvm.UnsignedAddValidatorTx{
BaseTx: platformvm.BaseTx{
BaseTx: avax.BaseTx{
NetworkID: avTx.NetworkID,
BlockchainID: avTx.BlockchainID,
Outs: utxoToTransferableOutput(avTx.Outs),
Ins: avTx.Ins,
Memo: avTx.Memo,
},
},
Validator: avTx.Validator,
Stake: avTx.Stake,
RewardsOwner: avTx.RewardsOwner,
Shares: avTx.Shares,
}
transactionHash = avTx.TxID.String()

case *ParsedCreateChainTx:
unsignedTx = &platformvm.UnsignedCreateChainTx{
BaseTx: platformvm.BaseTx{
BaseTx: avax.BaseTx{
NetworkID: avTx.NetworkID,
BlockchainID: avTx.BlockchainID,
Outs: utxoToTransferableOutput(avTx.Outs),
Ins: avTx.Ins,
Memo: avTx.Memo,
},
},
SubnetID: avTx.SubnetID,
ChainName: avTx.ChainName,
VMID: avTx.VMID,
FxIDs: avTx.FxIDs,
GenesisData: avTx.GenesisData,
SubnetAuth: avTx.SubnetAuth,
}
transactionHash = avTx.TxID.String()
default:
return nil, errUnexpectedGenesisTx
}

transaction, err := pmapper.ParseTx(unsignedTx, false)
if err != nil {
return nil, err
}

transaction.TransactionIdentifier = &types.TransactionIdentifier{
Hash: transactionHash,
}
transactions = append(transactions, transaction)
}
return transactions, nil
}

func utxoToTransferableOutput(utxos []*avax.UTXO) []*avax.TransferableOutput {
var outputs []*avax.TransferableOutput
for _, utxo := range utxos {
output := &avax.TransferableOutput{
Asset: avax.Asset{ID: utxo.AssetID()},
Out: utxo.Out.(avax.TransferableOut),
}
outputs = append(outputs, output)
}
return outputs
}
30 changes: 29 additions & 1 deletion service/backend/pchain/indexer/parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import (
"testing"
"time"

"github.com/ava-labs/avalanchego/utils/constants"

mocks "github.com/ava-labs/avalanche-rosetta/mocks/client"

"github.com/ava-labs/avalanchego/api"
Expand Down Expand Up @@ -44,7 +46,7 @@ func TestMain(m *testing.M) {

pchainClient := &mocks.PChainClient{}

pchainClient.On("GetNetworkID", mock.Anything).Return(uint32(1), nil).Once()
pchainClient.On("GetNetworkID", mock.Anything).Return(constants.MainnetID, nil).Once()

for _, idx := range idxs {
ret := readFixture("ins/%v.json", idx)
Expand Down Expand Up @@ -112,6 +114,32 @@ func TestGenesisBlockCreateChainTxs(t *testing.T) {
a.JSONEq(string(ret), string(j))
}

func TestGenesisBlockParseTxs(t *testing.T) {
a := assert.New(t)

pchainClient := &mocks.PChainClient{}
pchainClient.On("GetNetworkID", mock.Anything).Return(constants.FujiID, nil).Once()
p, err := NewParser(pchainClient)
if err != nil {
panic(err)
}

ctx := context.Background()
g, err := p.Initialize(ctx)
p.writeTime(time.Unix(0, 0))

rosettaTransactions, err := p.GenesisToTransactions(g)
assert.Nil(t, err)

j, err := stdjson.Marshal(rosettaTransactions)
if err != nil {
panic(err)
}

ret := readFixture("outs/genesis_fuji_rosetta.json")
a.JSONEq(string(ret), string(j))
}

func TestFixtures(t *testing.T) {
ctx := context.Background()
a := assert.New(t)
Expand Down
Loading

0 comments on commit 7dcec0d

Please sign in to comment.