Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

internal operations #365

Open
wants to merge 29 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
95b1716
internal operations (WIP)
kroggen Jun 4, 2024
a6f0e27
log operations and calls
kroggen Oct 2, 2024
6499ed5
save internal operations on db
kroggen Oct 3, 2024
0223910
delete internal operations on reorg
kroggen Oct 3, 2024
26ffd71
add RPC endpoint for internal operations
kroggen Oct 3, 2024
52be58a
fix build
kroggen Oct 3, 2024
4e234e3
log contract events and governance
kroggen Oct 3, 2024
2f022fe
fix RPC endpoint
kroggen Oct 4, 2024
2b750f2
compile protobuf grpc files
kroggen Oct 4, 2024
85be365
fix storing result of internal operation
kroggen Oct 4, 2024
ccfbc89
aergocli: add mock for GetInternalOperations
kroggen Oct 14, 2024
3a1bb8f
include the first call on internal operations
kroggen Oct 24, 2024
d6c107f
fix check for error on deploy
kroggen Oct 24, 2024
c811c48
aergocli: add query of internal operations
kroggen Oct 25, 2024
4466926
omit empty fields of internal operations
kroggen Oct 25, 2024
43e57b0
return every call to a contract
kroggen Oct 25, 2024
c950805
fix logging internal call
kroggen Oct 25, 2024
0b34675
log all call arguments individually
kroggen Oct 29, 2024
f458255
add some logs
kroggen Oct 29, 2024
117ce2c
add integration tests for internal operations
kroggen Oct 29, 2024
904c61c
config: add option to log internal ops
kroggen Nov 9, 2024
9d04169
integration tests: add config to log internal ops
kroggen Nov 9, 2024
2e36137
integration tests: wait until all nodes are stopped
kroggen Sep 24, 2024
3172fcc
Merge branch 'develop' into topic/internal-operations
kroggen Nov 11, 2024
3c65cb5
aergocli: fix duplicate GetInternalOperations mock
kroggen Nov 11, 2024
ceff79d
add amount to internal calls
kroggen Dec 7, 2024
ad6b716
Merge branch 'develop' into topic/internal-operations
kroggen Dec 7, 2024
3929663
omit amount if zero
kroggen Dec 7, 2024
f24358d
update integration test
kroggen Dec 7, 2024
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
32 changes: 27 additions & 5 deletions chain/chaindb.go
Original file line number Diff line number Diff line change
Expand Up @@ -521,7 +521,7 @@ func (cdb *ChainDB) dropBlock(dropNo types.BlockNo) error {
}

// remove receipt
cdb.deleteReceipts(&dbTx, dropBlock.BlockHash(), dropBlock.BlockNo())
cdb.deleteReceiptsAndOperations(&dbTx, dropBlock.BlockHash(), dropBlock.BlockNo())

// remove (hash/block)
dbTx.Delete(dropBlock.BlockHash())
Expand Down Expand Up @@ -666,6 +666,11 @@ func (cdb *ChainDB) checkExistReceipts(blockHash []byte, blockNo types.BlockNo)
return true
}

func (cdb *ChainDB) getInternalOperations(blockNo types.BlockNo) string {
data := cdb.store.Get(dbkey.InternalOps(blockNo))
return string(data)
}

type ChainTree struct {
Tree []ChainInfo
}
Expand All @@ -692,18 +697,35 @@ func (cdb *ChainDB) GetChainTree() ([]byte, error) {
return jsonBytes, nil
}

func (cdb *ChainDB) writeReceipts(blockHash []byte, blockNo types.BlockNo, receipts *types.Receipts) {
func (cdb *ChainDB) writeReceiptsAndOperations(block *types.Block, receipts *types.Receipts, internalOps string) {
hasReceipts := len(receipts.Get()) != 0
hasInternalOps := len(internalOps) != 0

if !hasReceipts && !hasInternalOps {
return
}

dbTx := cdb.store.NewTx()
defer dbTx.Discard()

val, _ := gob.Encode(receipts)
dbTx.Set(dbkey.Receipts(blockHash, blockNo), val)
blockHash := block.BlockHash()
blockNo := block.BlockNo()

if hasReceipts {
val, _ := gob.Encode(receipts)
dbTx.Set(dbkey.Receipts(blockHash, blockNo), val)
}

if hasInternalOps {
hayarobi marked this conversation as resolved.
Show resolved Hide resolved
dbTx.Set(dbkey.InternalOps(blockNo), []byte(internalOps))
}

dbTx.Commit()
}

func (cdb *ChainDB) deleteReceipts(dbTx *db.Transaction, blockHash []byte, blockNo types.BlockNo) {
func (cdb *ChainDB) deleteReceiptsAndOperations(dbTx *db.Transaction, blockHash []byte, blockNo types.BlockNo) {
(*dbTx).Delete(dbkey.Receipts(blockHash, blockNo))
(*dbTx).Delete(dbkey.InternalOps(blockNo))
}

func (cdb *ChainDB) writeReorgMarker(marker *ReorgMarker) error {
Expand Down
27 changes: 22 additions & 5 deletions chain/chainhandle.go
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,20 @@ func (cs *ChainService) listEvents(filter *types.FilterInfo) ([]*types.Event, er
return events, nil
}

func (cs *ChainService) getInternalOperations(blockNo types.BlockNo) (string, error) {
blockInMainChain, err := cs.cdb.GetBlockByNo(blockNo)
if err != nil {
return "", &ErrNoBlock{blockNo}
}

block, err := cs.cdb.getBlock(blockInMainChain.BlockHash())
if !bytes.Equal(block.BlockHash(), blockInMainChain.BlockHash()) {
return "", errors.New("internal operations not found")
}

return cs.cdb.getInternalOperations(blockNo), nil
}

type chainProcessor struct {
*ChainService
block *types.Block // starting block
Expand Down Expand Up @@ -811,9 +825,7 @@ func (cs *ChainService) executeBlock(bstate *state.BlockState, block *types.Bloc
return err
}

if len(ex.BlockState.Receipts().Get()) != 0 {
cs.cdb.writeReceipts(block.BlockHash(), block.BlockNo(), ex.BlockState.Receipts())
}
cs.cdb.writeReceiptsAndOperations(block, ex.BlockState.Receipts(), ex.BlockState.InternalOps())

cs.notifyEvents(block, ex.BlockState)

Expand Down Expand Up @@ -1007,11 +1019,12 @@ func executeTx(execCtx context.Context, ccc consensus.ChainConsensusCluster, cdb

var txFee *big.Int
var rv string
var internalOps string
var events []*types.Event

switch txBody.Type {
case types.TxType_NORMAL, types.TxType_TRANSFER, types.TxType_CALL, types.TxType_MULTICALL, types.TxType_DEPLOY, types.TxType_REDEPLOY:
rv, events, txFee, err = contract.Execute(execCtx, bs, cdb, tx.GetTx(), sender, receiver, bi, executionMode, false)
rv, events, internalOps, txFee, err = contract.Execute(execCtx, bs, cdb, tx.GetTx(), sender, receiver, bi, executionMode, false)
sender.SubBalance(txFee)
case types.TxType_GOVERNANCE:
txFee = new(big.Int).SetUint64(0)
Expand Down Expand Up @@ -1039,7 +1052,7 @@ func executeTx(execCtx context.Context, ccc consensus.ChainConsensusCluster, cdb
}
return types.ErrNotAllowedFeeDelegation
}
rv, events, txFee, err = contract.Execute(execCtx, bs, cdb, tx.GetTx(), sender, receiver, bi, executionMode, true)
rv, events, _, txFee, err = contract.Execute(execCtx, bs, cdb, tx.GetTx(), sender, receiver, bi, executionMode, true)
hayarobi marked this conversation as resolved.
Show resolved Hide resolved
receiver.SubBalance(txFee)
}

Expand Down Expand Up @@ -1094,6 +1107,10 @@ func executeTx(execCtx context.Context, ccc consensus.ChainConsensusCluster, cdb
}
bs.BpReward.Add(&bs.BpReward, txFee)

if len(internalOps) > 0 {
bs.AddInternalOps(internalOps)
}

receipt := types.NewReceipt(receiver.ID(), status, rv)
receipt.FeeUsed = txFee.Bytes()
receipt.TxHash = tx.GetHash()
Expand Down
10 changes: 9 additions & 1 deletion chain/chainservice.go
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ type IChainHandler interface {
getReceipt(txHash []byte) (*types.Receipt, error)
getReceipts(blockHash []byte) (*types.Receipts, error)
getReceiptsByNo(blockNo types.BlockNo) (*types.Receipts, error)
getInternalOperations(blockNo types.BlockNo) (string, error)
getAccountVote(addr []byte) (*types.AccountVoteInfo, error)
getVotes(id string, n uint32) (*types.VoteList, error)
getStaking(addr []byte) (*types.Staking, error)
Expand Down Expand Up @@ -296,7 +297,7 @@ func NewChainService(cfg *cfg.Config) *ChainService {
contract.TraceBlockNo = cfg.Blockchain.StateTrace
contract.SetStateSQLMaxDBSize(cfg.SQL.MaxDbSize)
contract.StartLStateFactory((cfg.Blockchain.NumWorkers+2)*(int(contract.MaxCallDepth(cfg.Hardfork.Version(math.MaxUint64)))+2), cfg.Blockchain.NumLStateClosers, cfg.Blockchain.CloseLimit)
contract.InitContext(cfg.Blockchain.NumWorkers + 2)
contract.InitContext(cfg.Blockchain.NumWorkers + 2, cfg.RPC.LogInternalOperations)

// For a strict governance transaction validation.
types.InitGovernance(cs.ConsensusType(), cs.IsPublic())
Expand Down Expand Up @@ -444,6 +445,7 @@ func (cs *ChainService) Receive(context actor.Context) {
*message.GetReceipt,
*message.GetReceipts,
*message.GetReceiptsByNo,
*message.GetInternalOperations,
*message.GetABI,
*message.GetQuery,
*message.GetStateQuery,
Expand Down Expand Up @@ -812,6 +814,12 @@ func (cw *ChainWorker) Receive(context actor.Context) {
Receipts: receipts,
Err: err,
})
case *message.GetInternalOperations:
operations, err := cw.getInternalOperations(msg.BlockNo)
context.Respond(message.GetInternalOperationsRsp{
Operations: operations,
Err: err,
})
case *message.GetABI:
sdb = cw.sdb.OpenNewStateDB(cw.sdb.GetRoot())
address, err := getAddressNameResolved(sdb, msg.Contract)
Expand Down
2 changes: 1 addition & 1 deletion chain/reorg.go
Original file line number Diff line number Diff line change
Expand Up @@ -491,7 +491,7 @@ func (reorg *reorganizer) rollback() error {
func (reorg *reorganizer) deleteOldReceipts() {
dbTx := reorg.cs.cdb.NewTx()
for _, blk := range reorg.oldBlocks {
reorg.cs.cdb.deleteReceipts(&dbTx, blk.GetHash(), blk.BlockNo())
reorg.cs.cdb.deleteReceiptsAndOperations(&dbTx, blk.GetHash(), blk.BlockNo())
}
dbTx.Commit()
}
Expand Down
73 changes: 73 additions & 0 deletions cmd/aergocli/cmd/internal_operations.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/**
* @file
* @copyright defined in aergo/LICENSE.txt
*/

package cmd

import (
"context"
"encoding/json"
"log"

"github.com/aergoio/aergo/v2/internal/enc/base58"
aergorpc "github.com/aergoio/aergo/v2/types"
"github.com/spf13/cobra"
)

func init() {
operationsCmd := &cobra.Command{
Use: "operations tx_hash",
Short: "Get internal operations for a transaction",
Args: cobra.MinimumNArgs(1),
Run: func(cmd *cobra.Command, args []string) {
// Decode the transaction hash
txHash, err := base58.Decode(args[0])
if err != nil {
log.Fatal(err)
}

// Retrieve the receipt to get the block height
receipt, err := client.GetReceipt(context.Background(), &aergorpc.SingleBytes{Value: txHash})
if err != nil {
log.Fatal(err)
}

// Extract block height from the receipt
blockHeight := receipt.BlockNo

// Use block height to get internal operations
msg, err := client.GetInternalOperations(context.Background(), &aergorpc.BlockNumberParam{BlockNo: blockHeight})
if err != nil {
log.Fatal(err)
}

// Extract the internal operations for the specific transaction
var operations []map[string]interface{}
if err := json.Unmarshal([]byte(msg.Value), &operations); err != nil {
log.Fatal(err)
}

// Print the internal operations for the specific transaction
var ops string
for _, op := range operations {
if op["txhash"] == args[0] {
// Remove the txhash field
delete(op, "txhash")
// Marshal the operation to JSON for better readability
opJSON, err := json.MarshalIndent(op, "", " ")
if err != nil {
log.Fatal(err)
}
ops = string(opJSON)
}
}
if ops == "" {
cmd.Println("No internal operations found for this transaction.")
} else {
cmd.Println(ops)
}
},
}
rootCmd.AddCommand(operationsCmd)
}
3 changes: 3 additions & 0 deletions config/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ type RPCConfig struct {
NSKey string `mapstructure:"nskey" description:"Private Key file for RPC or REST API"`
NSCACert string `mapstructure:"nscacert" description:"CA Certificate file for RPC or REST API"`
NSAllowCORS bool `mapstructure:"nsallowcors" description:"Allow CORS to RPC or REST API"`
// Internal operations
LogInternalOperations bool `mapstructure:"internal_operations" description:"Log internal operations"`
}

// Web3Config defines configurations for web3 service
Expand Down Expand Up @@ -206,6 +208,7 @@ nscert = "{{.RPC.NSCert}}"
nskey = "{{.RPC.NSKey}}"
nscacert = "{{.RPC.NSCACert}}"
nsallowcors = {{.RPC.NSAllowCORS}}
internal_operations = {{.RPC.LogInternalOperations}}

[p2p]
# Set address and port to which the inbound peers connect, and don't set loopback address or private network unless used in local network
Expand Down
20 changes: 10 additions & 10 deletions contract/contract.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ func Execute(
bi *types.BlockHeaderInfo,
executionMode int,
isFeeDelegation bool,
) (rv string, events []*types.Event, usedFee *big.Int, err error) {
) (rv string, events []*types.Event, internalOps string, usedFee *big.Int, err error) {

var (
txBody = tx.GetBody()
Expand Down Expand Up @@ -106,9 +106,9 @@ func Execute(

// execute the transaction
if receiver.IsDeploy() {
rv, events, ctrFee, err = Create(contractState, txPayload, receiver.ID(), ctx)
rv, events, internalOps, ctrFee, err = Create(contractState, txPayload, receiver.ID(), ctx)
} else {
rv, events, ctrFee, err = Call(contractState, txPayload, receiver.ID(), ctx)
rv, events, internalOps, ctrFee, err = Call(contractState, txPayload, receiver.ID(), ctx)
}

// close the trace file
Expand All @@ -118,40 +118,40 @@ func Execute(

// check if the execution fee is negative
if ctrFee != nil && ctrFee.Sign() < 0 {
return "", events, usedFee, ErrVmStart
return "", events, internalOps, usedFee, ErrVmStart
}
// add the execution fee to the total fee
usedFee.Add(usedFee, ctrFee)

// check if the execution failed
if err != nil {
if isSystemError(err) {
return "", events, usedFee, err
return "", events, internalOps, usedFee, err
}
return "", events, usedFee, newVmError(err)
return "", events, internalOps, usedFee, newVmError(err)
}

// check for sufficient balance for fee
if isFeeDelegation {
if receiver.Balance().Cmp(usedFee) < 0 {
return "", events, usedFee, newVmError(types.ErrInsufficientBalance)
return "", events, internalOps, usedFee, newVmError(types.ErrInsufficientBalance)
}
} else {
if sender.Balance().Cmp(usedFee) < 0 {
return "", events, usedFee, newVmError(types.ErrInsufficientBalance)
return "", events, internalOps, usedFee, newVmError(types.ErrInsufficientBalance)
}
}

if !isMultiCall {
// save the contract state
err = statedb.StageContractState(contractState, bs.StateDB)
if err != nil {
return "", events, usedFee, err
return "", events, internalOps, usedFee, err
}
}

// return the result
return rv, events, usedFee, nil
return rv, events, internalOps, usedFee, nil
}

// check if the tx is valid and if the code should be executed
Expand Down
Loading
Loading