Skip to content

Commit a06bd36

Browse files
committed
Prevent Ownable2Step update after renounce
1 parent 1ec2fe0 commit a06bd36

2 files changed

Lines changed: 41 additions & 3 deletions

File tree

src/Neo.SmartContract.Framework/Ownable2Step.cs

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,12 @@ public abstract class Ownable2Step : SmartContract
4747
// The proposed owner awaiting acceptance. Absent (null) means no transfer is in flight.
4848
private const byte Prefix_PendingOwner = 0xFC;
4949

50+
// Separates "not initialized yet" from "initialized and later renounced".
51+
private const byte Prefix_OwnerInitialized = 0xFB;
52+
5053
private static LocalStorageMap OwnerStorage => new(Prefix_Owner);
5154
private static LocalStorageMap PendingOwnerStorage => new(Prefix_PendingOwner);
55+
private static LocalStorageMap OwnerInitializedStorage => new(Prefix_OwnerInitialized);
5256

5357
/// <summary>
5458
/// Returns the current owner, or <see langword="null"/> if ownership has been renounced
@@ -175,6 +179,7 @@ public static void RenounceOwnership()
175179

176180
UInt160? previousOwner = GetOwner();
177181
OwnerStorage.Delete(ByteString.Empty);
182+
OwnerInitializedStorage.Put(ByteString.Empty, true);
178183
PendingOwnerStorage.Delete(ByteString.Empty);
179184
OnOwnershipTransferred(previousOwner, null);
180185
}
@@ -191,15 +196,25 @@ public static void RenounceOwnership()
191196
/// </remarks>
192197
protected static void InitializeOwner(object? data, bool update)
193198
{
194-
if (update && GetOwner() is not null)
195-
return;
199+
if (update)
200+
{
201+
if (OwnerInitializedStorage.GetBoolean(ByteString.Empty))
202+
return;
203+
204+
if (GetOwner() is not null)
205+
{
206+
OwnerInitializedStorage.Put(ByteString.Empty, true);
207+
return;
208+
}
209+
}
196210

197211
data ??= Runtime.Transaction.Sender;
198212

199213
UInt160 initialOwner = (UInt160)data;
200214
ExecutionEngine.Assert(initialOwner.IsValidAndNotZero, "owner must be valid");
201215

202216
OwnerStorage.Put(ByteString.Empty, initialOwner);
217+
OwnerInitializedStorage.Put(ByteString.Empty, true);
203218
OnOwnershipTransferred(null, initialOwner);
204219
}
205220
}

tests/Neo.SmartContract.Framework.UnitTests/Ownable2StepTest.cs

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,7 @@ public void InitializeOwner_UpdateInitializesWhenOwnerMissing()
289289
var engine = CreateEngine();
290290
var contract = Deploy(engine, out _, out _);
291291

292-
contract.ClearOwnerForTest();
292+
contract.ClearOwnershipStateForTest();
293293
Assert.IsNull(contract.GetOwner());
294294

295295
contract.InitializeForTest(Bob.Account, true);
@@ -299,6 +299,20 @@ public void InitializeOwner_UpdateInitializesWhenOwnerMissing()
299299
Merge(contract);
300300
}
301301

302+
[TestMethod]
303+
public void InitializeOwner_UpdateDoesNotReinitializeAfterRenounce()
304+
{
305+
var engine = CreateEngine();
306+
var contract = Deploy(engine, out _, out _);
307+
308+
contract.RenounceOwnership();
309+
contract.InitializeForTest(Bob.Account, true);
310+
311+
Assert.IsNull(contract.GetOwner());
312+
Assert.ThrowsException<TestException>(() => contract.TransferOwnership(Charlie.Account));
313+
Merge(contract);
314+
}
315+
302316
[TestMethod]
303317
public void RePropose_SupersedesPending()
304318
{
@@ -418,6 +432,12 @@ public static void ClearOwnerForTest()
418432
{
419433
Storage.Delete(new byte[] { 0xFD });
420434
}
435+
436+
public static void ClearOwnershipStateForTest()
437+
{
438+
Storage.Delete(new byte[] { 0xFD });
439+
Storage.Delete(new byte[] { 0xFB });
440+
}
421441
}";
422442

423443
var tempFile = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid():N}.cs");
@@ -480,6 +500,9 @@ public abstract class OwnableTwoStepProxy(SmartContractInitialize initialize)
480500
[DisplayName("clearOwnerForTest")]
481501
public abstract void ClearOwnerForTest();
482502

503+
[DisplayName("clearOwnershipStateForTest")]
504+
public abstract void ClearOwnershipStateForTest();
505+
483506
[DisplayName("transferOwnership")]
484507
public abstract void TransferOwnership(UInt160 newOwner);
485508

0 commit comments

Comments
 (0)