Skip to content

Commit

Permalink
fix: keep oracle history by using address as the ID
Browse files Browse the repository at this point in the history
  • Loading branch information
Maikol committed Apr 26, 2024
1 parent 2e58289 commit e28d611
Show file tree
Hide file tree
Showing 13 changed files with 136 additions and 342 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@ config/addresses.ts
config/generatedAddresses.json
tests/.bin
.vscode
.latest.json
.latest.json
.docker
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"prep:test": "mustache ./config/test.json subgraph.template.yaml > subgraph.yaml"
},
"dependencies": {
"@graphprotocol/graph-cli": "0.61.0",
"@graphprotocol/graph-cli": "0.71.0",
"@graphprotocol/graph-ts": "0.30.0"
},
"devDependencies": {
Expand Down
11 changes: 8 additions & 3 deletions schema.graphql
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
type GlobalState @entity {
id: ID!
oracles: [Oracle!]! @derivedFrom(field: "state")
activeOracles: [Oracle!]!
}

type Oracle @entity {
id: String! # oracle_index
id: Bytes! # address
index: String! # oracle_index
state: GlobalState!
address: Bytes!
latestConfig: OracleConfiguration!
configurations: [OracleConfiguration!]! @derivedFrom(field: "oracle")
votes: [OracleVote!]! @derivedFrom(field: "oracle")
active: Boolean!
activeSince: BigInt!
activeUntil: BigInt! # 0 means active
}

type OracleVote @entity(immutable: true) {
Expand All @@ -20,7 +24,7 @@ type OracleVote @entity(immutable: true) {
timestamp: BigInt! # uint256
}

type OracleConfiguration @entity {
type OracleConfiguration @entity(immutable: true) {
id: ID!
oracle: Oracle!
commitHash: String!
Expand All @@ -33,4 +37,5 @@ type OracleConfiguration @entity {
subgraph: String!
subgraphAvailabilityManagerContract: String!
oracleIndex: String!
createdAt: BigInt!
}
1 change: 0 additions & 1 deletion src/constants.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
export let ORACLE_CONFIGURATION_ABI =
"(string,(string,string,string,string,string,string,string,string,string))"
export let TUPLE_OFFSET_HEX = "0x0000000000000000000000000000000000000000000000000000000000000020"
19 changes: 11 additions & 8 deletions src/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,31 @@
import { log, BigInt, Bytes } from "@graphprotocol/graph-ts";
import { StoreCache } from "./store-cache";
import { TUPLE_OFFSET_HEX } from './constants';

export function parseCalldata(calldata: Bytes): Bytes {
// Remove function signature
let dataWithoutSignature = calldata.toHexString().slice(10);
let result = TUPLE_OFFSET_HEX + dataWithoutSignature;
return Bytes.fromHexString(result);

// ethabi expects a tuple offset if your tuple contains dynamic data
// https://medium.com/@r2d2_68242/indexing-transaction-input-data-in-a-subgraph-6ff5c55abf20
let hexStringToDecode = '0x0000000000000000000000000000000000000000000000000000000000000020' +
dataWithoutSignature;
return Bytes.fromHexString(hexStringToDecode);
}

export function isSubmitterAllowed(
cache: StoreCache,
oracleIndex: String,
submitter: String,
submitter: Bytes
): boolean {
let oracle = cache.getOracle(oracleIndex);
return oracle.address.toHexString() == submitter;
let oracle = cache.getOracle(submitter);
return oracle.active && oracle.index == oracleIndex;
}


export function getOracleVoteId(
subgraphDeploymentID: String,
oracleIndex: String,
oracleAddress: String,
timestamp: String
): string {
return [subgraphDeploymentID, oracleIndex, timestamp].join("-") as string;
return [subgraphDeploymentID, oracleAddress, timestamp].join("-") as string;
}
20 changes: 13 additions & 7 deletions src/sao-data-edge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,18 @@ import { StoreCache } from "./store-cache";
import { ORACLE_CONFIGURATION_ABI } from "./constants";

export function handleLog(event: LogEvent): void {
let submitter = event.transaction.from.toHexString();
event.logIndex.toI32();
processPayload(submitter, event.params.data, event.transaction.hash.toHexString());
let submitter = event.transaction.from;
processPayload(submitter, event.params.data, event.transaction.hash.toHexString(), event.block);
}

export function processPayload(submitter: string, payload: Bytes, txHash: string): void {
log.warning("Processing payload. Submitter: {}", [submitter]);
export function processPayload(
submitter: Bytes,
payload: Bytes,
txHash: string,
block: ethereum.Block
): void {
let submitterAddress = submitter.toHexString();
log.warning("Processing payload. Submitter: {}", [submitterAddress]);

let cache = new StoreCache();

Expand All @@ -21,13 +26,13 @@ export function processPayload(submitter: string, payload: Bytes, txHash: string
let decodedOracleIndex = decodedConfig[8].toString();

if (!isSubmitterAllowed(cache, decodedOracleIndex, submitter)) {
log.error("Submitter not allowed: {}", [submitter]);
log.error("Submitter not allowed: {}", [submitterAddress]);
return;
}

log.info("Submitter allowed", []);

let oracle = cache.getOracle(decodedOracleIndex);
let oracle = cache.getOracle(submitter);
let config = cache.getOracleConfiguration(txHash);
config.oracle = oracle.id;
config.commitHash = decoded[0].toString();
Expand All @@ -40,6 +45,7 @@ export function processPayload(submitter: string, payload: Bytes, txHash: string
config.subgraph = decodedConfig[6].toString();
config.subgraphAvailabilityManagerContract = decodedConfig[7].toString();
config.oracleIndex = decodedOracleIndex;
config.createdAt = block.timestamp;

oracle.latestConfig = config.id;

Expand Down
9 changes: 5 additions & 4 deletions src/store-cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,20 @@ export class SafeMap<K, V> extends Map<K, V> {

export class StoreCache {
state: GlobalState;
oracles: SafeMap<String, Oracle>;
oracles: SafeMap<Bytes, Oracle>;
oraclesConfigs: SafeMap<String, OracleConfiguration>;
oracleVotes: SafeMap<String, OracleVote>;

constructor() {
let state = GlobalState.load("0");
if (state == null) {
state = new GlobalState("0");
state.activeOracles = [];
state.save();
}

this.state = state;
this.oracles = new SafeMap<String, Oracle>();
this.oracles = new SafeMap<Bytes, Oracle>();
this.oraclesConfigs = new SafeMap<String, OracleConfiguration>();
this.oracleVotes = new SafeMap<String, OracleVote>();
}
Expand All @@ -30,13 +31,13 @@ export class StoreCache {
return this.state;
}

getOracle(id: String): Oracle {
getOracle(id: Bytes): Oracle {
if (this.oracles.safeGet(id) == null) {
let oracle = Oracle.load(id);
if (oracle == null) {
oracle = new Oracle(id);
oracle.state = this.state.id;
oracle.address = Bytes.empty();
oracle.index = "";
}
this.oracles.set(id, oracle);
}
Expand Down
40 changes: 33 additions & 7 deletions src/subgraph-availability-manager.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { BigInt, log } from "@graphprotocol/graph-ts"

import {
OracleSet as OracleSetEvent,
OracleVote as OracleVoteEvent,
} from "../generated/SubgraphAvailabilityManager/SubgraphAvailabilityManager"

import { Oracle } from "../generated/schema"
import { StoreCache } from "./store-cache";
import { getOracleVoteId } from "./helpers";

Expand All @@ -12,22 +14,26 @@ export function handleOracleSet(event: OracleSetEvent): void {

let cache = new StoreCache();
let state = cache.getGlobalState();
let oracle = cache.getOracle(oracleIndex);
let oracle = cache.getOracle(oracleAddress);
oracle.state = state.id;
oracle.address = oracleAddress;
oracle.index = oracleIndex;
oracle.latestConfig = "";

oracle.active = true
oracle.activeSince = event.block.timestamp;
oracle.activeUntil = BigInt.fromI32(0);

replaceActiveOracle(oracle, cache);
cache.commitChanges();
}

export function handleOracleVote(event: OracleVoteEvent): void {
let subgraphDeploymentID = event.params.subgraphDeploymentID.toHexString();
let oracleIndex = event.params.oracleIndex.toString();
let oracleAddress = event.transaction.from.toHexString();
let timestamp = event.params.timestamp.toString();
let voteId = getOracleVoteId(subgraphDeploymentID, oracleIndex, timestamp);
let voteId = getOracleVoteId(subgraphDeploymentID, oracleAddress, timestamp);

let cache = new StoreCache();
let oracle = cache.getOracle(oracleIndex);
let oracle = cache.getOracle(event.transaction.from);
let oracleVote = cache.getOracleVote(voteId);
oracleVote.subgraphDeploymentID = event.params.subgraphDeploymentID
oracleVote.deny = event.params.deny
Expand All @@ -36,3 +42,23 @@ export function handleOracleVote(event: OracleVoteEvent): void {

cache.commitChanges();
}

function replaceActiveOracle(newOracle: Oracle, cache: StoreCache): void {
let state = cache.getGlobalState();
let activeOracles = cache.getGlobalState().activeOracles;

for (let i = 0; i < activeOracles.length; i++) {
let currectActiveOracle = cache.getOracle(activeOracles[i]);
// Replacement is done by oracle_index
if (currectActiveOracle.index == newOracle.index) {
activeOracles[i] = newOracle.id;
state.activeOracles = activeOracles;
currectActiveOracle.active = false;
currectActiveOracle.activeUntil = newOracle.activeSince;
return;
}
}

activeOracles.push(newOracle.id);
state.activeOracles = activeOracles;
}
6 changes: 0 additions & 6 deletions subgraph.template.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,10 @@ dataSources:
- name: SubgraphAvailabilityManager
file: ./abis/SubgraphAvailabilityManager.json
eventHandlers:
- event: NewOwnership(indexed address,indexed address)
handler: handleNewOwnership
- event: NewPendingOwnership(indexed address,indexed address)
handler: handleNewPendingOwnership
- event: OracleSet(indexed uint256,indexed address)
handler: handleOracleSet
- event: OracleVote(indexed bytes32,bool,indexed uint256,uint256)
handler: handleOracleVote
- event: VoteTimeLimitSet(uint256)
handler: handleVoteTimeLimitSet
file: ./src/subgraph-availability-manager.ts
- kind: ethereum
name: SAODataEdge
Expand Down
7 changes: 5 additions & 2 deletions tests/sao-data-edge.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
beforeAll,
afterAll
} from "matchstick-as/assembly/index"
import { newMockEvent } from "matchstick-as"
import { Bytes, Address, BigInt } from "@graphprotocol/graph-ts"
import { processPayload } from "../src/sao-data-edge"
import { handleOracleSet } from "../src/subgraph-availability-manager"
Expand All @@ -32,7 +33,8 @@ describe("Describe entity assertions", () => {
"22d4402000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000732356339616537000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000220000000000000000000000000000000000000000000000000000000000000026000000000000000000000000000000000000000000000000000000000000002c0000000000000000000000000000000000000000000000000000000000000034000000000000000000000000000000000000000000000000000000000000003800000000000000000000000000000000000000000000000000000000000000001340000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000053130303030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000331303000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003333030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003c657468657265756d2c657468657265756d2f636f6e74726163742c66696c652f697066732c73756273747265616d732c66696c652f6172776561766500000000000000000000000000000000000000000000000000000000000000000000004c68747470733a2f2f6170692e74686567726170682e636f6d2f7375626772617068732f6e616d652f677261706870726f746f636f6c2f67726170682d6e6574776f726b2d617262697472756d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010434f4e54524143545f414444524553530000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013200000000000000000000000000000000000000000000000000000000000000"
) as Bytes;

processPayload(oracleID, payloadBytes, txHash1);
let event = newMockEvent()
processPayload(oracleAddress, payloadBytes, txHash1, event.block);

assert.entityCount("OracleConfiguration", 0)
})
Expand All @@ -42,7 +44,8 @@ describe("Describe entity assertions", () => {
"22d4402000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000732356339616537000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000220000000000000000000000000000000000000000000000000000000000000026000000000000000000000000000000000000000000000000000000000000002c0000000000000000000000000000000000000000000000000000000000000034000000000000000000000000000000000000000000000000000000000000003800000000000000000000000000000000000000000000000000000000000000001340000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000053130303030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000331303000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003333030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003c657468657265756d2c657468657265756d2f636f6e74726163742c66696c652f697066732c73756273747265616d732c66696c652f6172776561766500000000000000000000000000000000000000000000000000000000000000000000004c68747470733a2f2f6170692e74686567726170682e636f6d2f7375626772617068732f6e616d652f677261706870726f746f636f6c2f67726170682d6e6574776f726b2d617262697472756d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010434f4e54524143545f414444524553530000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013000000000000000000000000000000000000000000000000000000000000000"
) as Bytes;

processPayload(oracleID, payloadBytes, txHash2);
let event = newMockEvent()
processPayload(oracleAddress, payloadBytes, txHash2, event.block);

assert.entityCount("OracleConfiguration", 1);
assert.fieldEquals("OracleConfiguration", txHash2, "commitHash", "25c9ae7")
Expand Down
5 changes: 4 additions & 1 deletion tests/subgraph-availability-manager-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,15 @@ export function createOracleSetEvent(
}

export function createOracleVoteEvent(
oracleAddress: Address,
subgraphDeploymentID: Bytes,
deny: boolean,
oracleIndex: BigInt,
timestamp: BigInt
): OracleVote {
let oracleVoteEvent = changetype<OracleVote>(newMockEvent())
let event = newMockEvent()
event.transaction.from = oracleAddress
let oracleVoteEvent = changetype<OracleVote>(event)

oracleVoteEvent.parameters = new Array()

Expand Down
35 changes: 29 additions & 6 deletions tests/subgraph-availability-manager.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,23 @@ import {
describe,
test,
clearStore,
afterAll
afterEach
} from "matchstick-as/assembly/index"
import { Address, BigInt } from "@graphprotocol/graph-ts"
import { Address, BigInt, log } from "@graphprotocol/graph-ts"
import { handleOracleSet, handleOracleVote } from "../src/subgraph-availability-manager"
import { createOracleSetEvent, createOracleVoteEvent } from "./subgraph-availability-manager-utils"
import { getOracleVoteId } from "../src/helpers"
import { Oracle, GlobalState } from "../generated/schema"

const oracleID = "0x0000000000000000000000000000000000000004"
const oracleAddress = Address.fromString(oracleID)
const newOracleID = "0x0000000000000000000000000000000000000006"
const newOracleAddress = Address.fromString(newOracleID)
const subgraphDeploymentID = "0x0000000000000000000000000000000000000005"
const subgraphDeploymentAddress = Address.fromString(subgraphDeploymentID)

describe("ORACLE", () => {
afterAll(() => {
afterEach(() => {
clearStore()
})

Expand All @@ -25,7 +28,26 @@ describe("ORACLE", () => {
handleOracleSet(newOracleSetEvent)

assert.entityCount("Oracle", 1)
assert.fieldEquals("Oracle", "0", "address", oracleID)
assert.fieldEquals("Oracle", oracleID, "index", "0")
})

test("Replace oracle", () => {
let newOracleSetEvent = createOracleSetEvent(BigInt.fromI32(0), oracleAddress)
handleOracleSet(newOracleSetEvent)

// Replace oracle
let newOracleSetEvent2 = createOracleSetEvent(BigInt.fromI32(0), newOracleAddress)
handleOracleSet(newOracleSetEvent2)

assert.entityCount("Oracle", 2)
let oldOracle = Oracle.load(oracleAddress)!
let newOracle = Oracle.load(newOracleAddress)!
assert.booleanEquals(oldOracle.active, false)
assert.booleanEquals(newOracle.active, true)

let state = GlobalState.load("0")!
let activeOracles = state.activeOracles
assert.i32Equals(activeOracles.length, 1)
})

test("OracleVote created and stored", () => {
Expand All @@ -35,18 +57,19 @@ describe("ORACLE", () => {
assert.entityCount("Oracle", 1)

let newOracleVoteEvent = createOracleVoteEvent(
oracleAddress,
subgraphDeploymentAddress,
true,
BigInt.fromI32(0),
BigInt.fromI32(300)
)
handleOracleVote(newOracleVoteEvent)

let oracleVoteID = getOracleVoteId(subgraphDeploymentID, "0", "300");
let oracleVoteID = getOracleVoteId(subgraphDeploymentID, oracleID, "300");
assert.entityCount("OracleVote", 1)
assert.fieldEquals("OracleVote", oracleVoteID, "subgraphDeploymentID", subgraphDeploymentID)
assert.fieldEquals("OracleVote", oracleVoteID, "deny", "true")
assert.fieldEquals("OracleVote", oracleVoteID, "oracle", "0")
assert.fieldEquals("OracleVote", oracleVoteID, "oracle", oracleID)
assert.fieldEquals("OracleVote", oracleVoteID, "timestamp", "300")
})
})
Loading

0 comments on commit e28d611

Please sign in to comment.